/****************************** (C) COPYRIGHT 2016 RSEStudio ****************************
* File Name          : 
* Author             : RSES_Liyang
* Version            : V1.0.0
* Date               : 00-May-18
* Description        : 
*****************************************************************************************/
/* Includes ----------------------------------------------------------------------------*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include <string.h>			// memset
// #include "esp_log.h"

#include "Bsp.h"
#include "main.h"
#include "device_pins.h"
#include "W25X.h"
#include "MEAS.H"

//	Hardware spi pin macro definition
#define W25_HOST        SPI2_HOST
#define PIN_NUM_MISO    GPIO_NUM_47
#define PIN_NUM_MOSI    GPIO_NUM_48
#define PIN_NUM_CLK     GPIO_NUM_45
#define PIN_NUM_CS      GPIO_NUM_10

/* Private define ----------------------------------------------------------------------*/
#define RECODER_FILE_VERSION		35
/****************************************
	W25X32	����
****************************************/
#define W25X_ResetEnable            0x66
#define W25X_ResetMemory            0x99

#define W25X_DeviceID				0x9E
#define W25X_JedecDeviceID			0x9F

#define W25X_ReadData				0x03
#define W25X_FastReadData			0x0B

#define W25X_ReadDate4              0x13
#define W25X_FastRead4              0x0C

#define W25X_WriteEnable			0x06
#define W25X_WriteDisable			0x04

#define W25X_ReadStatusReg			0x05
#define W25X_WriteStatusReg3		0x11
#define W25X_ReadStatusReg3			0x15
#define W25X_ReadFlagRes            0x70
#define W25X_ReadNvConfReg          0xB5
#define W25X_Enter4ByteAddrMode		0xB7
#define W25X_Exit4ByteAddrMode		0xE9
#define W25X_ReadVConfReg           0x85
#define W25X_ReadEhVConfigReg       0x65
#define W25X_ReadExtendAddrReg      0xC8
#define W25X_ReadGenPurReg          0x96

#define W25X_WriteStatusReg			0x01
#define W25X_WriteNvConfReg         0xB1
#define W25X_WriteVConfReg          0x81
#define W25X_WriteEhVConfReg        0x61
#define W25X_WriteExtendAddrReg     0xC5

#define W25X_ClearFlagStatusReg     0x50

#define W25X_PageProgram			0x02
#define W25X_PageProgram4           0x12

#define W25X_4KErase                0x20
#define W25X_32KErase               0x52
#define W25X_64KErase               0xD8
#define W25X_DIEErase               0xC4
#define W25X_64KErase4              0xDC
#define W25X_32KErase4              0x5C
#define W25X_4KErase4               0x21

#define W25X_Enter4ByteMode         0xB7
#define W25X_Exit4ByteMode          0xE9

#define W25X_EnterDeepMode          0xB9
#define W25X_ExitDeepMode           0xAB


#define EF_START_ADDR             NVM_LOG_START_ADDR
#define EF_ERASE_MIN_SIZE         4096

#define ENV_AREA_SIZE             (EF_ERASE_MIN_SIZE * 2)

#define LOG_AREA_SIZE             NVM_LOG_AREA_SIZE    

#define FLASH_WRITE_GRAN            256
/* Private macro -----------------------------------------------------------------------*/
int16_t mem_lock_flag;

#define EF_ALIGN(size, align)           (((size) + (align) - 1) & ~((align) - 1))
#define EF_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))

#define MEM_LOCK()					xSemaphoreTake(w25x_lock,portMAX_DELAY);
#define MEM_UNLOCK()				xSemaphoreGive(w25x_lock);

#define EF_DEBUG(...)

#define EF_ASSERT(EXPR)                                                       \
if (!(EXPR))                                                                  \
{                                                                             \
    while (1);                                                                \
}

/* Extern variables --------------------------------------------------------------------*/

file_info_t file_info_buffer_new;
file_info_simple_t file_info_simple[DATA_RECODER_TOTAL_ITEM];

/* Private typedef ---------------------------------------------------------------------*/
spi_device_handle_t spi_handle;  // spi设备句柄
spi_transaction_t Transmission;  // spi事务结构体
static uint8_t Rx_W25_Data8_t;   // 接收w25x数据缓存

SemaphoreHandle_t w25x_lock = NULL;     // 互斥量句柄
StaticSemaphore_t xw25x_lock;  // 存储互斥量的数据结构

/* the flash sector current status */
typedef enum {
    EF_SECTOR_EMPTY,
    EF_SECTOR_USING,
    EF_SECTOR_FULL,
} EfSecrorStatus;


/* Private variables -------------------------------------------------------------------*/
unsigned int W25X_PAGE_SIZE         = 256;
unsigned int W25X_SECTOR_SIZE       = 4096;
unsigned int W25X_SECTOR_COUNT      = 4096;
unsigned int W25X_BLOCK_SIZE        = 65536;
unsigned int W25X_PAGES_PER_SECTOR  = 16;
/* Private functions -------------------------------------------------------------------*/
EfErrCode ef_log_init(void);
EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size);
EfErrCode ef_log_write(const uint32_t *log, size_t size);
EfErrCode ef_log_clean(void);
size_t ef_log_get_used_size(void);


//uint8_t test_buf_1[16];
//uint8_t test_buf_2[16];
//uint8_t test_buf_3[16];
/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/

void erase_meas_info(void)
{
    W25X_Erase_Sector(MEAS_INFO_ADDR);
}

void erase_all_headinfo(void)
{
	int i,addr;
	for(i=0;i<DATA_RECODER_TOTAL_ITEM;i++)
	{
		addr = i*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR;
		W25X_Erase_Sector(addr);
	}
}

void del_group_file_id(uint32_t id)
{
	int addr;
	if(id>DATA_RECODER_TOTAL_ITEM)
		return ;
	
	addr = id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR;
	W25X_Erase_Sector(addr);
}

void SPI_port_init(void)
{
    // 使用软件模拟cs
    gpio_config_t w25qx_cs = {
        .pin_bit_mask = (1ULL << PIN_NUM_CS),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,  // 上拉，默认高电平
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE
    };
    gpio_config(&w25qx_cs);

	// 初始状态：CS拉高（未选中芯片）
    gpio_set_level(PIN_NUM_CS, 1);

    // 配置SPI总线参数结构体
    spi_bus_config_t buscfg = {
        .miso_io_num = PIN_NUM_MISO,      // MISO引脚定义（主机输入，从机输出）
        .mosi_io_num = PIN_NUM_MOSI,      // MOSI引脚定义（主机输出，从机输入）
        .sclk_io_num = PIN_NUM_CLK,       // SCLK引脚定义（时钟信号）
        .quadwp_io_num = -1,              // Quad模式的WP引脚，-1表示不使用
        .quadhd_io_num = -1,              // Quad模式的HD引脚，-1表示不使用
        .max_transfer_sz = 4096,          // 最大传输大小为4096字节
    };
    
    // 初始化SPI总线，使用自动分配的DMA通道
    // W25_HOST：SPI主机号
    // &buscfg：SPI总线配置结构体指针
    // SPI_DMA_CH_AUTO：自动选择DMA通道
    ESP_ERROR_CHECK(spi_bus_initialize(W25_HOST, &buscfg, SPI_DMA_CH_AUTO));  // 检查初始化是否成功，失败则自动中止程序
    
    // 配置SPI设备接口参数
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 20 * 1000 * 1000,	// SPI时钟频率设置为20MHz
        .mode = 0,							// SPI模式0（CPOL=0, CPHA=0）
        .spics_io_num = -1,					// 片选引脚定义
        .queue_size = 5,					// 事务队列大小，设置为1表示最多缓存1个事务
        .pre_cb = NULL,						// 事务前回调函数，NULL表示不使用
        .post_cb = NULL,					// 事务后回调函数，NULL表示不使用
    };
    
    // 将W25Q64设备添加到SPI总线上
    // W25_HOST：SPI主机号
    // &devcfg：设备配置结构体指针
    // &spi_handle：返回的设备句柄，用于后续SPI操作
    ESP_ERROR_CHECK(spi_bus_add_device(W25_HOST, &devcfg, &spi_handle));  // 检查添加设备是否成功，失败则自动中止程序
}


uint8_t SerialFLASH_initialize(void)
{

	w25x_lock = xSemaphoreCreateMutexStatic(&xw25x_lock);
	if (w25x_lock == NULL)
	{
		while(1);
	}
	
	MEM_UNLOCK();	

	SPI_port_init();
	vTaskDelay(pdMS_TO_TICKS(50));
  
    W25X_Reset();
    
    vTaskDelay(pdMS_TO_TICKS(50));
    	
    ReadDeviceID();
	
    ef_log_init();
    
//W25X_Erase_Chip();
//erase_all_headinfo();
  
//	W25X_Read_Addr(0x1000,test_buf_1,16);
//	W25X_Erase_Sector(0x1000);
//	W25X_Read_Addr(0x1000,test_buf_2,16);
//	W25X_WriteSector_Addr(0x1000,test_buf_1,16);
//	W25X_Read_Addr(0x1000,test_buf_3,16);
    
	return 0;
}


//==================================================================
//��������SerialFLASH_Disk_Status
//���ߣ�  LY
//���ڣ�  2013-11-20
//���ܣ�  ����FLASH�豸״̬
//���������	NONE
//����ֵ�� 		0:�ɹ�
//�޸ļ�¼��	NONE
//==================================================================
uint8_t SerialFLASH_Disk_Status(void)
{
	return 0;
}


static uint8_t SPI_Send_Byte(uint8_t Data)
{
	Transmission.length = 8;
	Transmission.tx_buffer = &Data;
	Transmission.rx_buffer = &Rx_W25_Data8_t;
	ESP_ERROR_CHECK(spi_device_transmit(spi_handle, &Transmission));  // 检查传输是否成功，失败则自动中止程序

	return Rx_W25_Data8_t;
}

void W25X_Reset(void)
{
	MEM_LOCK();
	
    W25X_CS_L();
    SPI_Send_Byte(W25X_ResetEnable);
    SPI_Send_Byte(W25X_ResetMemory);
    W25X_CS_H();
    W25X_Wait_Busy();
	
	MEM_UNLOCK();
}

//==================================================================
//��������SerialFLASH_Disk_read
//���ߣ�  LY
//���ڣ�  2013-11-20
//���ܣ�  ����FLASH�豸״̬
//���������	NONE
//����ֵ�� 		0:�ɹ�
//�޸ļ�¼��	NONE
//==================================================================
void W25X_Read_Addr(uint32_t Addr, uint8_t* pBuffer,uint16_t Size)
{	
    uint16_t i;    
    W25X_Wait_Busy();    
    W25X_CS_L();
	
	SPI_Send_Byte(W25X_ReadData);	// 3字节地址写入
	// SPI_Send_Byte(W25X_ReadDate4);
	// SPI_Send_Byte((uint8_t)(Addr >> 24));
	SPI_Send_Byte((uint8_t)(Addr >> 16));
	SPI_Send_Byte((uint8_t)(Addr >> 8));
	SPI_Send_Byte((uint8_t)(Addr >> 0));

	
	for(i=0;i<Size;i++)
	{	
		pBuffer[i] = SPI_Send_Byte(0xff);
	}
	W25X_CS_H();
}

void W25X_Erase_Sector(uint32_t Addr)
{
    W25X_Wait_Busy();
	W25X_Write_Enable();
	W25X_CS_L();

	SPI_Send_Byte(W25X_4KErase);	// 3字节地址擦除
	// SPI_Send_Byte(W25X_4KErase4);
	// SPI_Send_Byte((uint8_t)(Addr >> 24));
	SPI_Send_Byte((uint8_t)(Addr >> 16));
	SPI_Send_Byte((uint8_t)(Addr >> 8));
	SPI_Send_Byte((uint8_t)(Addr >> 0));

	W25X_CS_H();
}


void W25X_Erase_Chip(void)
{
	W25X_Wait_Busy();
	W25X_Write_Enable();
	W25X_CS_L();
	SPI_Send_Byte(0xc7);
	W25X_CS_H();
	W25X_Wait_Busy();
}




void W25X_WriteSector_Addr(uint32_t Addr, uint8_t* pBuffer,int32_t Size)
{
	int i;
    int data_size;
    uint8_t *p_data;

    p_data = pBuffer;

    while(Size > 0)
    {
        
        if(Addr % FLASH_WRITE_GRAN!=0)
        {
            if(Size > (FLASH_WRITE_GRAN - (Addr % FLASH_WRITE_GRAN)))
            {
                data_size = FLASH_WRITE_GRAN - (Addr % FLASH_WRITE_GRAN);
            }
            else
            {
                data_size = Size;
            }
        }
        else
        {
            if(Size > FLASH_WRITE_GRAN)
            {
                data_size = FLASH_WRITE_GRAN;
            }
            else
            {
                data_size = Size;
            }
        }
        
        W25X_Wait_Busy();
        W25X_Write_Enable();
        
        W25X_CS_L();

        SPI_Send_Byte(W25X_PageProgram);	// 3字节地址写入
        // SPI_Send_Byte(W25X_PageProgram4);
        // SPI_Send_Byte((uint8_t)(Addr >> 24));
        SPI_Send_Byte((uint8_t)(Addr >> 16));
        SPI_Send_Byte((uint8_t)(Addr >> 8));
        SPI_Send_Byte((uint8_t)(Addr >> 0));


        for(i=0;i<data_size;i++)
        {				
            SPI_Send_Byte(*pBuffer++);
        }
        W25X_CS_H();

               
        Size -= data_size;
        Addr += data_size;
        p_data += data_size;        
    }
}


void W25X_Write_Enable(void)
{	
	W25X_CS_L();  
	SPI_Send_Byte(W25X_WriteEnable);
	W25X_CS_H();  
}

//��ȡSPI_FLASH��״̬�Ĵ���
//BIT7  6   5   4   3   2   1   0
//Bus   ES  ER PR  Res PS  PrS AS
uint8_t W25X_ReadFlagStatusReg(void)
{	
	uint8_t Status = 0;
	W25X_CS_L();
	SPI_Send_Byte(W25X_ReadStatusReg);
	Status = SPI_Send_Byte(0xff);
	W25X_CS_H();	
	return Status;
}

//uint8_t W25X_ReadStatusRegister3(void)
//{
//	uint8_t Status = 0;
//    
//	W25X_CS_L();
//	SPI_Send_Byte(W25X_ReadStatusReg3);
//	Status = SPI_Send_Byte(0xff);
//	W25X_CS_H();	
//	return Status;
//}
//int32_t w25x_wait_test;
void W25X_Wait_Busy(void)
{	 
//	int32_t time_out;
//	time_out = 0;
	
	while((W25X_ReadFlagStatusReg()&0x01)!=0)
    {
//		time_out++;
		// __nop();
		// __nop();
		// __nop();
		// __nop();
		vTaskDelay(pdMS_TO_TICKS(1));
    }
//	w25x_wait_test = time_out;
}


//==================================================================
//��������ReadDeviceId 
//���ߣ�  LY
//���ڣ�  2012-12-17
//���ܣ�  ��ȡFLASHоƬ���������Լ�����ID
//���������	0 ����������  1������ID
//����ֵ�� 		�������̻�����ID
//�޸ļ�¼��	NONE
//==================================================================
uint8_t ReadDeviceID(void)
{
    uint8_t result = 0;
	uint8_t ID1,ID2,ID3;
	
	W25X_CS_L();
	SPI_Send_Byte(W25X_JedecDeviceID);
	ID1 = SPI_Send_Byte(0xff);
	ID2 = SPI_Send_Byte(0xff);
	ID3 = SPI_Send_Byte(0xff);
	W25X_CS_H();
	
    if((ID1 == 0xFF)&&(ID2 == 0xFF))
    {
        result = 1;
    }
    
	if((ID1 !=0XEF)||(ID1 != 0xC2)) 
	{
		result = 2;
	}
	
	
	switch(ID2)
	{
		case 0x40:;break;
		default:;break;
	}
    ID3 = ID3;
    result = 0;
    
  
    return result;
}




//==================================================================
//��������ChipSHDN 
//���ߣ�  LY
//���ڣ�  2012-12-18
//���ܣ�  �������ģʽ
//���������	NONE
//����ֵ�� 		0�������ɹ�	1��оƬæ
//�޸ļ�¼��	NONE
//==================================================================
uint8_t ChipSHDN(void)
{	
	W25X_CS_L();
	SPI_Send_Byte(W25X_EnterDeepMode);      //0x20 send Block Erase command
	W25X_CS_H();	
	return 0;
}

//==================================================================
//��������ChipPWUP
//���ߣ�  LY
//���ڣ�  2012-12-18
//���ܣ�  ����оƬ����
//���������	NONE
//����ֵ�� 		0�������ɹ�	1������ʧ��
//�޸ļ�¼��	NONE
//==================================================================
uint8_t ChipPWUP(void)
{
	W25X_CS_L();
	SPI_Send_Byte(W25X_ExitDeepMode);      
	W25X_CS_H();
	return 0;
}


void W25X_Write_Addr(uint32_t addr,uint8_t *buf,uint16_t size)
{
	int i;
	W25X_Wait_Busy();
	W25X_Write_Enable();
	W25X_CS_L();
	
#if DEV_TYPE == 3
	SPI_Send_Byte(W25X_PageProgram);
	SPI_Send_Byte((uint8_t)(addr >> 16));
	SPI_Send_Byte((uint8_t)(addr >> 8));
	SPI_Send_Byte((uint8_t)(addr >> 0));
#else
	SPI_Send_Byte(W25X_PageProgram);
	// SPI_Send_Byte(W25X_PageProgram4);
	// SPI_Send_Byte((uint8_t)(addr >> 24));
	SPI_Send_Byte((uint8_t)(addr >> 16));
	SPI_Send_Byte((uint8_t)(addr >> 8));
	SPI_Send_Byte((uint8_t)(addr >> 0));
#endif
	for(i=0;i<size;i++)
	{				
		SPI_Send_Byte(buf[i]);
	}

	W25X_CS_H();
}

uint8_t flash_global_buf[260];
/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void Mem_MeasParams_Load(void)
{
	int i;
	//uint16_t crc16;
	uint8_t *params_buf;
	params_buf = flash_global_buf;
	
	MEM_LOCK();
	
	W25X_Read_Addr(MEAS_INFO_ADDR,params_buf,128);
	
	if(CRC16Software(params_buf,128)!=0)
	{
		iProfile.meas_params.SamplingSpan = 0;
		iProfile.meas_params.Start_Cond = 0;
		iProfile.meas_params.Start_Params_1 = 0;
		iProfile.meas_params.Start_Params_2 = 0;
		iProfile.meas_params.Start_Params_3 = 0;
		iProfile.meas_params.Start_Params_4 = 0;
		iProfile.meas_params.Stop_Cond = 0;
		iProfile.meas_params.Stop_Params_1 = 0;
		iProfile.meas_params.Stop_Params_2 = 0;
	    iProfile.meas_params.Stop_Params_3 = 0;
//		WWDG_SetCounter(0);
//		W25X_Erase_Sector(MEAS_INFO_ADDR);
		
		for(i=0;i<128;i++)
		{
			params_buf[i] = 0;
		}
		
		*(uint32_t*)(&params_buf[0]) = iProfile.meas_params.SamplingSpan;
		*(uint32_t*)(&params_buf[4]) =	iProfile.meas_params.Start_Cond;
		*(uint32_t*)(&params_buf[8]) = iProfile.meas_params.Start_Params_1;
		*(uint32_t*)(&params_buf[12]) = iProfile.meas_params.Start_Params_2;
		*(uint32_t*)(&params_buf[16]) = iProfile.meas_params.Start_Params_3;
		*(uint32_t*)(&params_buf[20]) = iProfile.meas_params.Start_Params_4;
		*(uint32_t*)(&params_buf[24]) = iProfile.meas_params.Stop_Cond;
		*(uint32_t*)(&params_buf[28]) = iProfile.meas_params.Stop_Params_1;
		*(uint32_t*)(&params_buf[32]) = iProfile.meas_params.Stop_Params_2;
		*(uint32_t*)(&params_buf[36]) = iProfile.meas_params.Stop_Params_3;
		
//		crc16 = CRC16Software(params_buf,126);
//		*(uint16_t*)(&params_buf[126]) = crc16;
//		WWDG_SetCounter(0);
//		W25X_WriteSector_Addr(MEAS_INFO_ADDR,params_buf,128);
	}
	else
	{
		iProfile.meas_params.SamplingSpan	= *(uint32_t*)(&params_buf[0]) ;
		iProfile.meas_params.Start_Cond		= *(uint16_t*)(&params_buf[4]) ;
		iProfile.meas_params.Start_Params_1	= *(uint16_t*)(&params_buf[8]) ;
		iProfile.meas_params.Start_Params_2	= *(uint16_t*)(&params_buf[12]);
		iProfile.meas_params.Start_Params_3	= *(uint16_t*)(&params_buf[16]);
		iProfile.meas_params.Start_Params_4	= *(uint32_t*)(&params_buf[20]);
		iProfile.meas_params.Stop_Cond		= *(uint32_t*)(&params_buf[24]);
		iProfile.meas_params.Stop_Params_1	= *(uint32_t*)(&params_buf[28]);
		iProfile.meas_params.Stop_Params_2	= *(uint32_t*)(&params_buf[32]);
		iProfile.meas_params.Stop_Params_3	= *(uint32_t*)(&params_buf[36]);
	}
	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : Mem_MeasParams_Save
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void Mem_MeasParams_Save(void)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	params_buf = flash_global_buf;
	MEM_LOCK();

	W25X_Erase_Sector(MEAS_INFO_ADDR);
		
	for(i=0;i<128;i++)
	{
		params_buf[i] = 0;
	}
	
	*(uint32_t*)(&params_buf[0]) =  iProfile.meas_params.SamplingSpan;
	*(uint32_t*)(&params_buf[4]) =	iProfile.meas_params.Start_Cond;
	*(uint32_t*)(&params_buf[8]) =  iProfile.meas_params.Start_Params_1;
	*(uint32_t*)(&params_buf[12]) = iProfile.meas_params.Start_Params_2;
	*(uint32_t*)(&params_buf[16]) = iProfile.meas_params.Start_Params_3;
	*(uint32_t*)(&params_buf[20]) = iProfile.meas_params.Start_Params_4;
	*(uint32_t*)(&params_buf[24]) = iProfile.meas_params.Stop_Cond;
	*(uint32_t*)(&params_buf[28]) = iProfile.meas_params.Stop_Params_1;
	*(uint32_t*)(&params_buf[32]) = iProfile.meas_params.Stop_Params_2;
	*(uint32_t*)(&params_buf[36]) = iProfile.meas_params.Stop_Params_3;
	
	crc16 = CRC16Software(params_buf,126);
	*(uint16_t*)(&params_buf[126]) = crc16;
	
	W25X_WriteSector_Addr(MEAS_INFO_ADDR,params_buf,128);
	
	MEM_UNLOCK();
}
//


/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
uint8_t *get_dev_mem(void)
{
	//int i;
	uint8_t *p_result_buf;
	//uint16_t crc16;
	uint8_t *params_buf;
	params_buf = flash_global_buf;
	
	MEM_LOCK();
	
	W25X_Read_Addr(NVM_6_ADDR_DEV_MEM,params_buf,256);
	
	if(CRC16Software(params_buf,256)!=0)
	{
		p_result_buf = 0;
	}
	else
	{
		p_result_buf = params_buf;
	}
	MEM_UNLOCK();
	return p_result_buf;
}
//

/*****************************************************************************************
* Function Name  : Mem_MeasParams_Save
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void set_dev_mem(uint8_t *p_buf,uint16_t len)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	params_buf = flash_global_buf;
	MEM_LOCK();

	W25X_Erase_Sector(NVM_6_ADDR_DEV_MEM);
		
	for(i=0;i<256;i++)
	{
		params_buf[i] = 0;
	}
	
	if(len>256)
		len = 256;
	
	for(i=0;i<len;i++)
	{
		params_buf[i] = p_buf[i];
	}
	
	crc16 = CRC16Software(params_buf,254);
	*(uint16_t*)(&params_buf[254]) = crc16;
	
	W25X_WriteSector_Addr(NVM_6_ADDR_DEV_MEM,params_buf,256);
	
	MEM_UNLOCK();
}
//

void iprofile_config_check(SYS_CONFIG *pConfig)
{
	if((pConfig->mfg_year<1)||(pConfig->mfg_year>99))
	{
		pConfig->mfg_year = 24;
	}	
	if((pConfig->mfg_month<1)||(pConfig->mfg_month>12))
	{
		pConfig->mfg_month = 1;
	}
	if((pConfig->mfg_day<1)||(pConfig->mfg_day>31))
	{
		pConfig->mfg_day = 1;
	}
	if((pConfig->Total_channel>TEMPERATURE_MAX_CHANNEL))
	{
		pConfig->Total_channel = 1;
	}
	
	if((pConfig->calib_year<1)||(pConfig->calib_year>99))
	{
		pConfig->calib_year = 24;
	}	
	if((pConfig->calib_month<1)||(pConfig->calib_month>12))
	{
		pConfig->calib_month = 1;
	}
	if((pConfig->calib_day<1)||(pConfig->calib_day>31))
	{
		pConfig->calib_day = 1;
	}
	
	if((pConfig->calib_next_year<1)||(pConfig->calib_next_year>99))
	{
		pConfig->calib_next_year = 24;
	}	
	if((pConfig->calib_next_month<1)||(pConfig->calib_next_month>12))
	{
		pConfig->calib_next_month = 1;
	}
	if((pConfig->calib_next_day<1)||(pConfig->calib_next_day>31))
	{
		pConfig->calib_next_day = 1;
	}
	
	if((pConfig->warranty_year<1)||(pConfig->warranty_year>99))
	{
		pConfig->warranty_year = 24;
	}	
	if((pConfig->warranty_month<1)||(pConfig->warranty_month>12))
	{
		pConfig->warranty_month = 1;
	}
	if((pConfig->warranty_day<1)||(pConfig->warranty_day>31))
	{
		pConfig->warranty_day = 1;
	}
	
	if((pConfig->over_temp_stop<30)||(pConfig->over_temp_stop>200))
	{
		pConfig->over_temp_stop = 79;
	}
	
	if((pConfig->start_max_temp<1)||(pConfig->start_max_temp>100))
	{
		pConfig->start_max_temp = 50;
	}
	
	if((pConfig->start_max_tc_temp<1)||(pConfig->start_max_tc_temp>200))
	{
		pConfig->start_max_tc_temp = 50;
	}
}
/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void Mem_device_config_Load(void)
{
	int i;
	
	uint8_t *params_buf;
	params_buf = flash_global_buf;
	
	MEM_LOCK();
	
	W25X_Read_Addr(NVM_0_ADDR_DEVICE_CONFIG,params_buf,128);
	
	if(CRC16Software(params_buf,128)!=0)
	{
		iProfile.conf.dev_main_type =   DEVICE_TYPE;
		iProfile.conf.dev_sub_type =    DEV_TYPE;
		iProfile.conf.dev_ext_type =    0;
		iProfile.conf.check_id =        0;
		iProfile.conf.version_Major =   FW_VER_MAJOR;
		iProfile.conf.version_minor = 	FW_VER_MINOR;
		iProfile.conf.version_revison = FW_VER_REVISION;
		iProfile.conf.version_label = 	FW_VER_LABEL;
		
		
		iProfile.conf.mfg_year =        23;
		iProfile.conf.mfg_month =       12;
		iProfile.conf.mfg_day =         11;
		iProfile.conf.Total_channel =   6;
		iProfile.conf.dummy_1 =         0;
		iProfile.conf.dummy_2 =         0;
		
		iProfile.conf.serial_id[0] = 'x';
		iProfile.conf.serial_id[1] = 'x';
		iProfile.conf.serial_id[2] = 'x';
		iProfile.conf.serial_id[3] = 'x';
		iProfile.conf.serial_id[4] = 'x';
		iProfile.conf.serial_id[5] = 'x';
		iProfile.conf.serial_id[6] = 'x';
		iProfile.conf.serial_id[7] = '6';
		iProfile.conf.serial_id[8] = 0;
		
		iProfile.conf.calib_year =          23;
		iProfile.conf.calib_month =         12;
		iProfile.conf.calib_day =           11;
		iProfile.conf.calib_next_year =     24;
		iProfile.conf.calib_next_month =    12;
		iProfile.conf.calib_next_day =      11;
		iProfile.conf.warranty_year =       24;
		iProfile.conf.warranty_month =      12;
		iProfile.conf.warranty_day =        11;
		
		iProfile.conf.dummy_3 =             0;
		iProfile.conf.over_temp_stop =      80;
		iProfile.conf.start_max_temp =      40;
		iProfile.conf.start_max_tc_temp =   40;
		
		iProfile.conf.dev_name[0] =         'A';
		iProfile.conf.dev_name[1] =         0;
		
		iProfile.conf.Bat_capacity =        100;
		
	}
	else
	{	
		iProfile.conf.dev_main_type =       DEVICE_TYPE;//params_buf[0]
		iProfile.conf.dev_sub_type =        DEV_TYPE;//params_buf[1]
		iProfile.conf.dev_ext_type =        params_buf[2];
		iProfile.conf.check_id =            params_buf[3];
		iProfile.conf.version_Major =       FW_VER_MAJOR;//params_buf[4]
		iProfile.conf.version_minor = 	    FW_VER_MINOR;//params_buf[5]
		iProfile.conf.version_revison =     FW_VER_REVISION;//params_buf[6]
		iProfile.conf.version_label = 	    FW_VER_LABEL;//params_buf[7]
		
		
		iProfile.conf.mfg_year =            params_buf[8];
		iProfile.conf.mfg_month =           params_buf[9];
		iProfile.conf.mfg_day =             params_buf[10];
		iProfile.conf.Total_channel =       params_buf[11];
		iProfile.conf.dummy_1 =             params_buf[12];
		iProfile.conf.dummy_2 =             params_buf[13];
        
        if(iProfile.conf.Total_channel > TEMPERATURE_MAX_CHANNEL)
        {
            iProfile.conf.Total_channel = TEMPERATURE_MAX_CHANNEL;
        }
		
		for(i=0;i<36;i++)
		{
			iProfile.conf.serial_id[i] =    params_buf[i + 14];
		}
		
		iProfile.conf.calib_year =          params_buf[50];
		iProfile.conf.calib_month =         params_buf[51];
		iProfile.conf.calib_day =           params_buf[52];
		iProfile.conf.calib_next_year =     params_buf[53];
		iProfile.conf.calib_next_month =    params_buf[54];
		iProfile.conf.calib_next_day =      params_buf[55];
		iProfile.conf.warranty_year =       params_buf[56];
		iProfile.conf.warranty_month =      params_buf[57];
		iProfile.conf.warranty_day =        params_buf[58];
		
		
		iProfile.conf.over_temp_stop = (params_buf[60]<<0)|(params_buf[61]<<8);
		iProfile.conf.start_max_temp = (params_buf[62]<<0)|(params_buf[63]<<8);
		iProfile.conf.start_max_tc_temp = (params_buf[64]<<0)|(params_buf[65]<<8);
		
		for(i=0;i<32;i++)
		{
			iProfile.conf.dev_name[i] = params_buf[i + 66];
		}

		iProfile.conf.Bat_capacity = (params_buf[98]<<0)|(params_buf[99]<<8);
		
		iprofile_config_check(&iProfile.conf);
		
	}
	
	if(iProfile.conf.serial_id[0] == 'V' && iProfile.conf.serial_id[1] == 'C' && iProfile.conf.serial_id[2] == 'A' && iProfile.conf.serial_id[3] == 'M' && iProfile.conf.serial_id[5] == '\0')
	{
		iProfile.sys_status.test_mode = 1;
	}
	else
	{
		iProfile.sys_status.test_mode = 0;
	}
	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : Mem_device_config_save
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void Mem_device_config_save(void)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	params_buf = flash_global_buf;
		
	for(i=0;i<128;i++)
	{
		params_buf[i] = 0;
	}
    
	
	params_buf[0] = iProfile.conf.dev_main_type;
	params_buf[1] = iProfile.conf.dev_sub_type;
	params_buf[2] = iProfile.conf.dev_ext_type;
	params_buf[3] = iProfile.conf.check_id;
	params_buf[4] = iProfile.conf.version_Major;
	params_buf[5] = iProfile.conf.version_minor;
	params_buf[6] = iProfile.conf.version_revison;
	params_buf[7] = iProfile.conf.version_label;
	
	params_buf[8] = iProfile.conf.mfg_year;
	params_buf[9] = iProfile.conf.mfg_month;
	params_buf[10] = iProfile.conf.mfg_day;
	params_buf[11] = iProfile.conf.Total_channel;
	params_buf[12] = iProfile.conf.dummy_1;
	params_buf[13] = iProfile.conf.dummy_2;
	
	for(i=0;i<36;i++)
	{
		params_buf[i+14] = iProfile.conf.serial_id[i];
	}
			
	params_buf[50] = iProfile.conf.calib_year;
	params_buf[51] = iProfile.conf.calib_month;
	params_buf[52] = iProfile.conf.calib_day;
	params_buf[53] = iProfile.conf.calib_next_year;
	params_buf[54] = iProfile.conf.calib_next_month;
	params_buf[55] = iProfile.conf.calib_next_day;
	params_buf[56] = iProfile.conf.warranty_year;
	params_buf[57] = iProfile.conf.warranty_month;
	params_buf[58] = iProfile.conf.warranty_day;
	
	
	params_buf[60] = (iProfile.conf.over_temp_stop>>0)&0xFF;
	params_buf[61] = (iProfile.conf.over_temp_stop>>8)&0xFF;
	
	params_buf[62] = (iProfile.conf.start_max_temp>>0)&0xFF;
	params_buf[63] = (iProfile.conf.start_max_temp>>8)&0xFF;
	
	params_buf[64] = (iProfile.conf.start_max_tc_temp>>0)&0xFF;
	params_buf[65] = (iProfile.conf.start_max_tc_temp>>8)&0xFF;

	for(i=0;i<32;i++)
	{
		params_buf[i + 66] = iProfile.conf.dev_name[i];
	}
	
	params_buf[98] = (iProfile.conf.Bat_capacity>>0)&0xFF;
	params_buf[99] = (iProfile.conf.Bat_capacity>>8)&0xFF;
	
	for(i=0;i<26;i++)
	{
		params_buf[i + 100] = 0;
	}
	
	crc16 = CRC16Software(params_buf,126);
	*(uint16_t*)(&params_buf[126]) = crc16;
	
	if(iProfile.conf.serial_id[0] == 'V' && iProfile.conf.serial_id[1] == 'C' && iProfile.conf.serial_id[2] == 'A' && iProfile.conf.serial_id[3] == 'M' && iProfile.conf.serial_id[5] == '\0')
	{
		iProfile.sys_status.test_mode = 1;
	}
	else
	{
		iProfile.sys_status.test_mode = 0;
	}
	
	MEM_LOCK();
	
	W25X_Erase_Sector(NVM_0_ADDR_DEVICE_CONFIG);
	
	vTaskDelay(pdMS_TO_TICKS(100));
	
	W25X_WriteSector_Addr(NVM_0_ADDR_DEVICE_CONFIG,params_buf,128);
	
	MEM_UNLOCK();	
}

/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_thermo_calib_Load(void)
{
	uint16_t i;
	uint8_t *params_buf;
	short temp_16;
	
	params_buf = flash_global_buf;
	
	MEM_LOCK();
	
	W25X_Read_Addr(NVM_1_ADDR_CALIB_TEMPERATURE,params_buf,256);

	if(CRC16Software(params_buf,256)!=0)
	{
		for(i=0;i<24;i++)
		{
			iProfile.temperature.calib[i].raw_higher =  5000;
			iProfile.temperature.calib[i].real_higher = 5000;
			iProfile.temperature.calib[i].raw_lower =   0;
			iProfile.temperature.calib[i].real_lower =  0;
			iProfile.temperature.calib[i].offset     =  0;
			
			calib_dpoint_cal(&iProfile.temperature.calib[i]);       
		}
	}
	else
	{	
		for(i=0;i<24;i++)
		{
			temp_16 = (params_buf[i*10 + 0]<<0) | (params_buf[i*10 + 1]<<8);
			iProfile.temperature.calib[i].raw_higher = temp_16;
			temp_16 = (params_buf[i*10 + 2]<<0) | (params_buf[i*10 + 3]<<8);
			iProfile.temperature.calib[i].real_higher = temp_16;
			temp_16 = (params_buf[i*10 + 4]<<0) | (params_buf[i*10 + 5]<<8);
			iProfile.temperature.calib[i].raw_lower =   temp_16;
			temp_16 = (params_buf[i*10 + 6]<<0) | (params_buf[i*10 + 7]<<8);
			iProfile.temperature.calib[i].real_lower =  temp_16;
			temp_16 = (params_buf[i*10 + 8]<<0) | (params_buf[i*10 + 9]<<8);
			iProfile.temperature.calib[i].offset     =  temp_16;
			
			calib_dpoint_cal(&iProfile.temperature.calib[i]);
		}
	}
	
	W25X_Read_Addr(NVM_1_ADDR_CALIB_TEMPERATURE + 256,params_buf,256);

	if(CRC16Software(params_buf,256)!=0)
	{
		for(i=0;i<12;i++)
		{
			iProfile.temperature.calib[i + 24].raw_higher =  5000;
			iProfile.temperature.calib[i + 24].real_higher = 5000;
			iProfile.temperature.calib[i + 24].raw_lower =   0;
			iProfile.temperature.calib[i + 24].real_lower =  0;
			iProfile.temperature.calib[i + 24].offset     =  0;
			
			calib_dpoint_cal(&iProfile.temperature.calib[i + 24]);           
		}
		
		iProfile.temperature.cold_calib[0].raw_higher =  5000;
		iProfile.temperature.cold_calib[0].real_higher = 5000;
		iProfile.temperature.cold_calib[0].raw_lower =   0;
		iProfile.temperature.cold_calib[0].real_lower =  0;
		iProfile.temperature.cold_calib[0].offset     =  0;
		
		calib_dpoint_cal(&iProfile.temperature.cold_calib[0]);      
	}
	else
	{	
		for(i=0;i<12;i++)
		{
			temp_16 = (params_buf[i*10 + 0]<<0) | (params_buf[i*10 + 1]<<8);
			iProfile.temperature.calib[24 + i].raw_higher =  temp_16;
			temp_16 = (params_buf[i*10 + 2]<<0) | (params_buf[i*10 + 3]<<8);
			iProfile.temperature.calib[24 + i].real_higher = temp_16;
			temp_16 = (params_buf[i*10 + 4]<<0) | (params_buf[i*10 + 5]<<8);
			iProfile.temperature.calib[24 + i].raw_lower =   temp_16;
			temp_16 = (params_buf[i*10 + 6]<<0) | (params_buf[i*10 + 7]<<8);
			iProfile.temperature.calib[24 + i].real_lower =  temp_16;
			temp_16 = (params_buf[i*10 + 8]<<0) | (params_buf[i*10 + 9]<<8);
			iProfile.temperature.calib[24 + i].offset     =  temp_16;
			
			calib_dpoint_cal(&iProfile.temperature.calib[24 + i]);
		}
		temp_16 = (params_buf[200 + 0]<<0) | (params_buf[200 + 1]<<8);
		iProfile.temperature.cold_calib[0].raw_higher 	=  temp_16;		
		temp_16 = (params_buf[200 + 2]<<0) | (params_buf[200 + 3]<<8);
		iProfile.temperature.cold_calib[0].real_higher 	=  temp_16;
		temp_16 = (params_buf[200 + 4]<<0) | (params_buf[200 + 5]<<8);
		iProfile.temperature.cold_calib[0].raw_lower 	=  temp_16;
		temp_16 = (params_buf[200 + 6]<<0) | (params_buf[200 + 7]<<8);
		iProfile.temperature.cold_calib[0].real_lower 	=  temp_16;
		temp_16 = (params_buf[200 + 8]<<0) | (params_buf[200 + 9]<<8);
		iProfile.temperature.cold_calib[0].offset     	=  temp_16;
		
		calib_dpoint_cal(&iProfile.temperature.cold_calib[0]);	
	}
	
	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_thermo_calib_save(void)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	
	params_buf = flash_global_buf;
	MEM_LOCK();
	
	W25X_Erase_Sector(NVM_1_ADDR_CALIB_TEMPERATURE);
	
	for(i=0;i<256;i++)
	{
		params_buf[i] = 0;
	}
	
	for(i=0;i<24;i++)
	{
        params_buf[i*10+0] = (iProfile.temperature.calib[i].raw_higher>>0)&0xFF;
        params_buf[i*10+1] = (iProfile.temperature.calib[i].raw_higher>>8)&0xFF;	
		
        params_buf[i*10+2] = (iProfile.temperature.calib[i].real_higher>>0)&0xFF;
        params_buf[i*10+3] = (iProfile.temperature.calib[i].real_higher>>8)&0xFF;
		
        params_buf[i*10+4] = (iProfile.temperature.calib[i].raw_lower>>0)&0xFF;
        params_buf[i*10+5] = (iProfile.temperature.calib[i].raw_lower>>8)&0xFF; 
		
        params_buf[i*10+6] = (iProfile.temperature.calib[i].real_lower>>0)&0xFF;
        params_buf[i*10+7] = (iProfile.temperature.calib[i].real_lower>>8)&0xFF;   

		params_buf[i*10+8] = (iProfile.temperature.calib[i].offset>>0)&0xFF;
        params_buf[i*10+9] = (iProfile.temperature.calib[i].offset>>8)&0xFF;   
	}
		
	crc16 = CRC16Software(params_buf,254);
	*(uint16_t*)(&params_buf[254]) = crc16;
	
	W25X_WriteSector_Addr(NVM_1_ADDR_CALIB_TEMPERATURE,params_buf,256);
	
	for(i=0;i<256;i++)
	{
		params_buf[i] = 0;
	}
	
	for(i=0;i<12;i++)
	{
        params_buf[i*10+0] = (iProfile.temperature.calib[24 + i].raw_higher>>0)&0xFF;
        params_buf[i*10+1] = (iProfile.temperature.calib[24 + i].raw_higher>>8)&0xFF;	
		
        params_buf[i*10+2] = (iProfile.temperature.calib[24 + i].real_higher>>0)&0xFF;
        params_buf[i*10+3] = (iProfile.temperature.calib[24 + i].real_higher>>8)&0xFF;
		
        params_buf[i*10+4] = (iProfile.temperature.calib[24 + i].raw_lower>>0)&0xFF;
        params_buf[i*10+5] = (iProfile.temperature.calib[24 + i].raw_lower>>8)&0xFF; 
		
        params_buf[i*10+6] = (iProfile.temperature.calib[24 + i].real_lower>>0)&0xFF;
        params_buf[i*10+7] = (iProfile.temperature.calib[24 + i].real_lower>>8)&0xFF;   

		params_buf[i*10+8] = (iProfile.temperature.calib[24 + i].offset>>0)&0xFF;
        params_buf[i*10+9] = (iProfile.temperature.calib[24 + i].offset>>8)&0xFF;   
	}
	
	params_buf[200+0] = (iProfile.temperature.cold_calib[0].raw_higher>>0)&0xFF;
	params_buf[200+1] = (iProfile.temperature.cold_calib[0].raw_higher>>8)&0xFF;	
	
	params_buf[200+2] = (iProfile.temperature.cold_calib[0].real_higher>>0)&0xFF;
	params_buf[200+3] = (iProfile.temperature.cold_calib[0].real_higher>>8)&0xFF;
	
	params_buf[200+4] = (iProfile.temperature.cold_calib[0].raw_lower>>0)&0xFF;
	params_buf[200+5] = (iProfile.temperature.cold_calib[0].raw_lower>>8)&0xFF; 
	
	params_buf[200+6] = (iProfile.temperature.cold_calib[0].real_lower>>0)&0xFF;
	params_buf[200+7] = (iProfile.temperature.cold_calib[0].real_lower>>8)&0xFF;   

	params_buf[200+8] = (iProfile.temperature.cold_calib[0].offset>>0)&0xFF;
	params_buf[200+9] = (iProfile.temperature.cold_calib[0].offset>>8)&0xFF;  
		
	crc16 = CRC16Software(params_buf,254);
	*(uint16_t*)(&params_buf[254]) = crc16;
	
	W25X_WriteSector_Addr(NVM_1_ADDR_CALIB_TEMPERATURE + 256,params_buf,256);

	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : nvm_calib_pressure_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_calib_pressure_Load(void)
{
	uint16_t i;
	uint8_t *params_buf;
	int   temp_32;
	
	params_buf = flash_global_buf;
	
	MEM_LOCK();

	W25X_Read_Addr(NVM_3_ADDR_CALIB_PRESSURE,params_buf,256);

	if(CRC16Software(params_buf,256)!=0)
	{
		for(i=0;i<1;i++)
		{
			iProfile.pressure.calib[i].raw_higher =  100000;
			iProfile.pressure.calib[i].real_higher = 100000;
			iProfile.pressure.calib[i].raw_lower =   0;
			iProfile.pressure.calib[i].real_lower =  0;
			iProfile.pressure.calib[i].offset     =  0;
			
			calib_dpoint_cal(&iProfile.pressure.calib[i]);       
		}   
	}
	else
	{	
		for(i=0;i<1;i++)
		{
			temp_32 = (params_buf[i*20 + 0]<<0) | (params_buf[i*20 + 1]<<8) | (params_buf[i*20 + 2]<<16) | (params_buf[i*20 + 3]<<24);
			iProfile.pressure.calib[i].raw_higher =  temp_32;
			
			temp_32 = (params_buf[i*20 + 4]<<0) | (params_buf[i*20 + 5]<<8) | (params_buf[i*20 + 6]<<16) | (params_buf[i*20 + 7]<<24);
			iProfile.pressure.calib[i].real_higher = temp_32;
			
			temp_32 = (params_buf[i*20 + 8]<<0) | (params_buf[i*20 + 9]<<8) | (params_buf[i*20 + 10]<<16) | (params_buf[i*20 + 11]<<24);
			iProfile.pressure.calib[i].raw_lower =   temp_32;
			
			temp_32 = (params_buf[i*20 + 12]<<0) | (params_buf[i*20 + 13]<<8) | (params_buf[i*20 + 14]<<16) | (params_buf[i*20 + 15]<<24);
			iProfile.pressure.calib[i].real_lower =  temp_32;
			
			temp_32 = (params_buf[i*20 + 16]<<0) | (params_buf[i*20 + 17]<<8) | (params_buf[i*20 + 18]<<16) | (params_buf[i*20 + 19]<<24);
			iProfile.pressure.calib[i].offset     =  temp_32;
			
			calib_dpoint_cal(&iProfile.pressure.calib[i]);
		}	
	}
	
	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_calib_pressure_save(void)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	
	params_buf = flash_global_buf;
	MEM_LOCK();
	
	W25X_Erase_Sector(NVM_3_ADDR_CALIB_PRESSURE);
	
	for(i=0;i<256;i++)
	{
		params_buf[i] = 0;
	}
	
	for(i=0;i<1;i++)
	{
        params_buf[i*20+0] = (iProfile.pressure.calib[i].raw_higher>>0)&0xFF;
        params_buf[i*20+1] = (iProfile.pressure.calib[i].raw_higher>>8)&0xFF;
		params_buf[i*20+2] = (iProfile.pressure.calib[i].raw_higher>>16)&0xFF;
        params_buf[i*20+3] = (iProfile.pressure.calib[i].raw_higher>>24)&0xFF;			
		
        params_buf[i*20+4] = (iProfile.pressure.calib[i].real_higher>>0)&0xFF;
        params_buf[i*20+5] = (iProfile.pressure.calib[i].real_higher>>8)&0xFF;
		params_buf[i*20+6] = (iProfile.pressure.calib[i].real_higher>>16)&0xFF;
        params_buf[i*20+7] = (iProfile.pressure.calib[i].real_higher>>24)&0xFF;
		
        params_buf[i*20+8] =  (iProfile.pressure.calib[i].raw_lower>>0)&0xFF;
        params_buf[i*20+9] =  (iProfile.pressure.calib[i].raw_lower>>8)&0xFF; 
		params_buf[i*20+10] = (iProfile.pressure.calib[i].raw_lower>>16)&0xFF;
        params_buf[i*20+11] = (iProfile.pressure.calib[i].raw_lower>>24)&0xFF; 
		
        params_buf[i*20+12] = (iProfile.pressure.calib[i].real_lower>>0)&0xFF;
        params_buf[i*20+13] = (iProfile.pressure.calib[i].real_lower>>8)&0xFF;   
		params_buf[i*20+14] = (iProfile.pressure.calib[i].real_lower>>16)&0xFF;
        params_buf[i*20+15] = (iProfile.pressure.calib[i].real_lower>>24)&0xFF;   

		params_buf[i*20+16] = (iProfile.pressure.calib[i].offset>>0)&0xFF;
        params_buf[i*20+17] = (iProfile.pressure.calib[i].offset>>8)&0xFF;  
		params_buf[i*20+18] = (iProfile.pressure.calib[i].offset>>16)&0xFF;
        params_buf[i*20+19] = (iProfile.pressure.calib[i].offset>>24)&0xFF;		
	}
		
	crc16 = CRC16Software(params_buf,254);
	*(uint16_t*)(&params_buf[254]) = crc16;
    
	W25X_WriteSector_Addr(NVM_3_ADDR_CALIB_PRESSURE,params_buf,256);

	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : nvm_calib_pressure_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_calib_oxygen_Load(void)
{
	//uint16_t i;
	uint8_t *params_buf;
	int   temp_32;
	
	params_buf = flash_global_buf;
	
	MEM_LOCK();

	W25X_Read_Addr(NVM_2_ADDR_CALIB_OXY,params_buf,256);

	if(CRC16Software(params_buf,256)!=0)
	{
		iProfile.oxygen.mdp_calib.calib_point = 5;
		
		iProfile.oxygen.mdp_calib.offset = 0;
		
		iProfile.oxygen.mdp_calib.p_x[0] = 48281;
		iProfile.oxygen.mdp_calib.p_y[0] = 100;
		
		iProfile.oxygen.mdp_calib.p_x[1] = 203131;
		iProfile.oxygen.mdp_calib.p_y[1] = 1000;
		
		iProfile.oxygen.mdp_calib.p_x[2] = 855221;
		iProfile.oxygen.mdp_calib.p_y[2] = 5000;
		
		iProfile.oxygen.mdp_calib.p_x[3] = 1670221;
		iProfile.oxygen.mdp_calib.p_y[3] = 10000;		

        iProfile.oxygen.mdp_calib.p_x[4] = 6560221;
		iProfile.oxygen.mdp_calib.p_y[4] = 40000;		        
	}
	else
	{	
		iProfile.oxygen.mdp_calib.calib_point = 5;	
		temp_32 = (params_buf[0 + 0]<<0) | (params_buf[0 + 1]<<8) | (params_buf[0 + 2]<<16) | (params_buf[0 + 3]<<24);		
		iProfile.oxygen.mdp_calib.offset = temp_32;
		
		temp_32 = (params_buf[4 + 0]<<0) | (params_buf[4 + 1]<<8) | (params_buf[4 + 2]<<16) | (params_buf[4 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_x[0] = temp_32;
		
		temp_32 = (params_buf[8 + 0]<<0) | (params_buf[8 + 1]<<8) | (params_buf[8 + 2]<<16) | (params_buf[8 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_y[0] = temp_32;
		
		temp_32 = (params_buf[12 + 0]<<0) | (params_buf[12 + 1]<<8) | (params_buf[12 + 2]<<16) | (params_buf[12 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_x[1] = temp_32;
		
		temp_32 = (params_buf[16 + 0]<<0) | (params_buf[16 + 1]<<8) | (params_buf[16 + 2]<<16) | (params_buf[16 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_y[1] = temp_32;
				
		temp_32 = (params_buf[20 + 0]<<0) | (params_buf[20 + 1]<<8) | (params_buf[20 + 2]<<16) | (params_buf[20 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_x[2] = temp_32;
		
		temp_32 = (params_buf[24 + 0]<<0) | (params_buf[24 + 1]<<8) | (params_buf[24 + 2]<<16) | (params_buf[24 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_y[2] = temp_32;
		
		temp_32 = (params_buf[28 + 0]<<0) | (params_buf[28 + 1]<<8) | (params_buf[28 + 2]<<16) | (params_buf[28 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_x[3] = temp_32;
		
		temp_32 = (params_buf[32 + 0]<<0) | (params_buf[32 + 1]<<8) | (params_buf[32 + 2]<<16) | (params_buf[32 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_y[3] = temp_32;	
        
        temp_32 = (params_buf[36 + 0]<<0) | (params_buf[36 + 1]<<8) | (params_buf[36 + 2]<<16) | (params_buf[36 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_x[4] = temp_32;
		
		temp_32 = (params_buf[40 + 0]<<0) | (params_buf[40 + 1]<<8) | (params_buf[40 + 2]<<16) | (params_buf[40 + 3]<<24);		
		iProfile.oxygen.mdp_calib.p_y[4] = temp_32;	
	}
	calib_mdpoint_cal(&iProfile.oxygen.mdp_calib);
	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : Mem_MeasParams_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_calib_oxygen_save(void)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	
	params_buf = flash_global_buf;
	MEM_LOCK();
	
	W25X_Erase_Sector(NVM_2_ADDR_CALIB_OXY);
	
	for(i=0;i<256;i++)
	{
		params_buf[i] = 0;
	}
	

	params_buf[0] = (iProfile.oxygen.mdp_calib.offset>>0)&0xFF;
	params_buf[1] = (iProfile.oxygen.mdp_calib.offset>>8)&0xFF;
	params_buf[2] = (iProfile.oxygen.mdp_calib.offset>>16)&0xFF;
	params_buf[3] = (iProfile.oxygen.mdp_calib.offset>>24)&0xFF;			
	
	params_buf[4] = (iProfile.oxygen.mdp_calib.p_x[0]>>0)&0xFF;
	params_buf[5] = (iProfile.oxygen.mdp_calib.p_x[0]>>8)&0xFF;
	params_buf[6] = (iProfile.oxygen.mdp_calib.p_x[0]>>16)&0xFF;
	params_buf[7] = (iProfile.oxygen.mdp_calib.p_x[0]>>24)&0xFF;
	
	params_buf[8] =  (iProfile.oxygen.mdp_calib.p_y[0]>>0)&0xFF;
	params_buf[9] =  (iProfile.oxygen.mdp_calib.p_y[0]>>8)&0xFF; 
	params_buf[10] = (iProfile.oxygen.mdp_calib.p_y[0]>>16)&0xFF;
	params_buf[11] = (iProfile.oxygen.mdp_calib.p_y[0]>>24)&0xFF; 
	
	params_buf[12] = (iProfile.oxygen.mdp_calib.p_x[1]>>0)&0xFF;
	params_buf[13] = (iProfile.oxygen.mdp_calib.p_x[1]>>8)&0xFF;
	params_buf[14] = (iProfile.oxygen.mdp_calib.p_x[1]>>16)&0xFF;
	params_buf[15] = (iProfile.oxygen.mdp_calib.p_x[1]>>24)&0xFF;
	
	params_buf[16] = (iProfile.oxygen.mdp_calib.p_y[1]>>0)&0xFF;
	params_buf[17] = (iProfile.oxygen.mdp_calib.p_y[1]>>8)&0xFF; 
	params_buf[18] = (iProfile.oxygen.mdp_calib.p_y[1]>>16)&0xFF;
	params_buf[19] = (iProfile.oxygen.mdp_calib.p_y[1]>>24)&0xFF; 

	params_buf[20] = (iProfile.oxygen.mdp_calib.p_x[2]>>0)&0xFF;
	params_buf[21] = (iProfile.oxygen.mdp_calib.p_x[2]>>8)&0xFF;
	params_buf[22] = (iProfile.oxygen.mdp_calib.p_x[2]>>16)&0xFF;
	params_buf[23] = (iProfile.oxygen.mdp_calib.p_x[2]>>24)&0xFF;
	
	params_buf[24] = (iProfile.oxygen.mdp_calib.p_y[2]>>0)&0xFF;
	params_buf[25] = (iProfile.oxygen.mdp_calib.p_y[2]>>8)&0xFF; 
	params_buf[26] = (iProfile.oxygen.mdp_calib.p_y[2]>>16)&0xFF;
	params_buf[27] = (iProfile.oxygen.mdp_calib.p_y[2]>>24)&0xFF; 	
	
	params_buf[28] = (iProfile.oxygen.mdp_calib.p_x[3]>>0)&0xFF;
	params_buf[29] = (iProfile.oxygen.mdp_calib.p_x[3]>>8)&0xFF;
	params_buf[30] = (iProfile.oxygen.mdp_calib.p_x[3]>>16)&0xFF;
	params_buf[31] = (iProfile.oxygen.mdp_calib.p_x[3]>>24)&0xFF;
	
	params_buf[32] = (iProfile.oxygen.mdp_calib.p_y[3]>>0)&0xFF;
	params_buf[33] = (iProfile.oxygen.mdp_calib.p_y[3]>>8)&0xFF; 
	params_buf[34] = (iProfile.oxygen.mdp_calib.p_y[3]>>16)&0xFF;
	params_buf[35] = (iProfile.oxygen.mdp_calib.p_y[3]>>24)&0xFF; 	
    
    params_buf[36] = (iProfile.oxygen.mdp_calib.p_x[4]>>0)&0xFF;
	params_buf[37] = (iProfile.oxygen.mdp_calib.p_x[4]>>8)&0xFF;
	params_buf[38] = (iProfile.oxygen.mdp_calib.p_x[4]>>16)&0xFF;
	params_buf[39] = (iProfile.oxygen.mdp_calib.p_x[4]>>24)&0xFF;
	
	params_buf[40] = (iProfile.oxygen.mdp_calib.p_y[4]>>0)&0xFF;
	params_buf[41] = (iProfile.oxygen.mdp_calib.p_y[4]>>8)&0xFF; 
	params_buf[42] = (iProfile.oxygen.mdp_calib.p_y[4]>>16)&0xFF;
	params_buf[43] = (iProfile.oxygen.mdp_calib.p_y[4]>>24)&0xFF; 	
    
    params_buf[44] = (iProfile.oxygen.mdp_calib.p_x[5]>>0)&0xFF;
	params_buf[45] = (iProfile.oxygen.mdp_calib.p_x[5]>>8)&0xFF;
	params_buf[46] = (iProfile.oxygen.mdp_calib.p_x[5]>>16)&0xFF;
	params_buf[47] = (iProfile.oxygen.mdp_calib.p_x[5]>>24)&0xFF;
	
	params_buf[48] = (iProfile.oxygen.mdp_calib.p_y[5]>>0)&0xFF;
	params_buf[49] = (iProfile.oxygen.mdp_calib.p_y[5]>>8)&0xFF; 
	params_buf[50] = (iProfile.oxygen.mdp_calib.p_y[5]>>16)&0xFF;
	params_buf[51] = (iProfile.oxygen.mdp_calib.p_y[5]>>24)&0xFF; 	
		
	crc16 = CRC16Software(params_buf,254);
	*(uint16_t*)(&params_buf[254]) = crc16;

	W25X_WriteSector_Addr(NVM_2_ADDR_CALIB_OXY,params_buf,256);

	MEM_UNLOCK();	
}
//

/*****************************************************************************************
* Function Name  : nvm_calib_angle_Load
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_calib_angle_Load(void)
{
	uint16_t i;
	uint8_t *params_buf;
	int   temp_32;
	
	params_buf = flash_global_buf;
	
	MEM_LOCK();

	W25X_Read_Addr(NVM_7_ADDR_CALIB_VIB_ANGLE,params_buf,256);

	if(CRC16Software(params_buf,256)!=0)
	{
		for(i=0;i<2;i++)
		{
			iProfile.vibrate.calib[i].raw_higher =  100000;
			iProfile.vibrate.calib[i].real_higher = 100000;
			iProfile.vibrate.calib[i].raw_lower =   0;
			iProfile.vibrate.calib[i].real_lower =  0;
			iProfile.vibrate.calib[i].offset     =  0;
			
			calib_dpoint_cal(&iProfile.vibrate.calib[i]);       
		}   
        
        iProfile.vibrate.calib[2].raw_higher =  16300;
        iProfile.vibrate.calib[2].real_higher = 500;
        iProfile.vibrate.calib[2].raw_lower =   0;
        iProfile.vibrate.calib[2].real_lower =  0;
        iProfile.vibrate.calib[2].offset     =  0;
        
        calib_dpoint_cal(&iProfile.vibrate.calib[2]);   

        iProfile.vibrate.calib[3].raw_higher =  16300;
        iProfile.vibrate.calib[3].real_higher = 500;
        iProfile.vibrate.calib[3].raw_lower =   0;
        iProfile.vibrate.calib[3].real_lower =  0;
        iProfile.vibrate.calib[3].offset     =  0;
        
        calib_dpoint_cal(&iProfile.vibrate.calib[3]); 

        iProfile.vibrate.calib[4].raw_higher =  16300;
        iProfile.vibrate.calib[4].real_higher = 500;
        iProfile.vibrate.calib[4].raw_lower =   0;
        iProfile.vibrate.calib[4].real_lower =  0;
        iProfile.vibrate.calib[4].offset     =  0;
        
        calib_dpoint_cal(&iProfile.vibrate.calib[4]);         
            
	}
	else
	{	
		for(i=0;i<5;i++)
		{
			temp_32 = (params_buf[i*20 + 0]<<0) | (params_buf[i*20 + 1]<<8) | (params_buf[i*20 + 2]<<16) | (params_buf[i*20 + 3]<<24);
			iProfile.vibrate.calib[i].raw_higher =  temp_32;
			
			temp_32 = (params_buf[i*20 + 4]<<0) | (params_buf[i*20 + 5]<<8) | (params_buf[i*20 + 6]<<16) | (params_buf[i*20 + 7]<<24);
			iProfile.vibrate.calib[i].real_higher = temp_32;
			
			temp_32 = (params_buf[i*20 + 8]<<0) | (params_buf[i*20 + 9]<<8) | (params_buf[i*20 + 10]<<16) | (params_buf[i*20 + 11]<<24);
			iProfile.vibrate.calib[i].raw_lower =   temp_32;
			
			temp_32 = (params_buf[i*20 + 12]<<0) | (params_buf[i*20 + 13]<<8) | (params_buf[i*20 + 14]<<16) | (params_buf[i*20 + 15]<<24);
			iProfile.vibrate.calib[i].real_lower =  temp_32;
			
			temp_32 = (params_buf[i*20 + 16]<<0) | (params_buf[i*20 + 17]<<8) | (params_buf[i*20 + 18]<<16) | (params_buf[i*20 + 19]<<24);
			iProfile.vibrate.calib[i].offset     =  temp_32;
			
			calib_dpoint_cal(&iProfile.vibrate.calib[i]);
		}	
	}
	
	MEM_UNLOCK();	
}
//


/*****************************************************************************************
* Function Name  : nvm_calib_angle_save
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void nvm_calib_angle_save(void)
{
	int i;
	uint16_t crc16;
	uint8_t *params_buf;
	
	params_buf = flash_global_buf;
	MEM_LOCK();
	
	W25X_Erase_Sector(NVM_7_ADDR_CALIB_VIB_ANGLE);
	
	for(i=0;i<256;i++)
	{
		params_buf[i] = 0;
	}
	
	for(i=0;i<5;i++)
	{
        params_buf[i*20+0] = (iProfile.vibrate.calib[i].raw_higher>>0)&0xFF;
        params_buf[i*20+1] = (iProfile.vibrate.calib[i].raw_higher>>8)&0xFF;
		params_buf[i*20+2] = (iProfile.vibrate.calib[i].raw_higher>>16)&0xFF;
        params_buf[i*20+3] = (iProfile.vibrate.calib[i].raw_higher>>24)&0xFF;			
		
        params_buf[i*20+4] = (iProfile.vibrate.calib[i].real_higher>>0)&0xFF;
        params_buf[i*20+5] = (iProfile.vibrate.calib[i].real_higher>>8)&0xFF;
		params_buf[i*20+6] = (iProfile.vibrate.calib[i].real_higher>>16)&0xFF;
        params_buf[i*20+7] = (iProfile.vibrate.calib[i].real_higher>>24)&0xFF;
		
        params_buf[i*20+8] =  (iProfile.vibrate.calib[i].raw_lower>>0)&0xFF;
        params_buf[i*20+9] =  (iProfile.vibrate.calib[i].raw_lower>>8)&0xFF; 
		params_buf[i*20+10] = (iProfile.vibrate.calib[i].raw_lower>>16)&0xFF;
        params_buf[i*20+11] = (iProfile.vibrate.calib[i].raw_lower>>24)&0xFF; 
		
        params_buf[i*20+12] = (iProfile.vibrate.calib[i].real_lower>>0)&0xFF;
        params_buf[i*20+13] = (iProfile.vibrate.calib[i].real_lower>>8)&0xFF;   
		params_buf[i*20+14] = (iProfile.vibrate.calib[i].real_lower>>16)&0xFF;
        params_buf[i*20+15] = (iProfile.vibrate.calib[i].real_lower>>24)&0xFF;   

		params_buf[i*20+16] = (iProfile.vibrate.calib[i].offset>>0)&0xFF;
        params_buf[i*20+17] = (iProfile.vibrate.calib[i].offset>>8)&0xFF;  
		params_buf[i*20+18] = (iProfile.vibrate.calib[i].offset>>16)&0xFF;
        params_buf[i*20+19] = (iProfile.vibrate.calib[i].offset>>24)&0xFF;		
	}
		
	crc16 = CRC16Software(params_buf,254);
	*(uint16_t*)(&params_buf[254]) = crc16;
	
	W25X_WriteSector_Addr(NVM_7_ADDR_CALIB_VIB_ANGLE,params_buf,256);

	MEM_UNLOCK();	
}
//


/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
extern uint8_t *simpleProfile_RealTime_Buf;

void Mem_FileHead_Init(void)
{
	uint16_t id;
	uint32_t Start_Date,Temp32,faddr,i;
	uint32_t End_Date;
	uint16_t Group;
	uint16_t speed,ch,item_base;
	uint8_t *pFileHeadBuf;
	uint16_t crc16;
	pFileHeadBuf = flash_global_buf;
  
	for(id=0;id<DATA_RECODER_TOTAL_ITEM;id++)
	{
		file_info_simple[id].Used = 0;

		W25X_Read_Addr(id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR,pFileHeadBuf,256);
		
//		if((CRC16Software(pFileHeadBuf,128)!=0)||(CRC16Software(pFileHeadBuf+128,128)!=0))
//		{
//			continue;
//		}
    
		if(CRC16Software(pFileHeadBuf,128)!=0)
		{
			continue;
		}
    
		Group = pFileHeadBuf[4];
		if(Group>DATA_RECODER_TOTAL_ITEM)
		{
			continue;
		}

		Start_Date =  (pFileHeadBuf[8]<<0)|(pFileHeadBuf[9]<<8)|(pFileHeadBuf[10]<<16)|(pFileHeadBuf[11]<<24);        
		
		if(CRC16Software(pFileHeadBuf+128,128)!=0)
		{
			speed = *((uint16_t*)&pFileHeadBuf[18]);
			ch    = *((uint8_t*)&pFileHeadBuf[14]);
			
            
			if (ch <= 12)
			{
				ch = 1;
			}	
			else if (ch <= 24)
			{
				ch = 2;
			}	
			else if (ch <= 36)
			{
				ch = 3;
			}
			else
			{
				continue;
			}
            
            item_base = *((uint8_t*)&pFileHeadBuf[15]);
            if((item_base<1)||(item_base > 10))
            {
                continue;
            }
			
			for(i=0;i<128;i++)
			{
				if(pFileHeadBuf[128+i]!=0xFF)
				{
					break;
				}
			}
			if(i<128)
			{
				continue;
			}
					
			faddr = id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR + 256;
	
			W25X_Read_Addr(faddr+0,pFileHeadBuf,32);
			Temp32 = *(uint32_t*)&pFileHeadBuf[0];
            Temp32 = Temp32&0x00FFFFFF;
			if((Temp32 != 0))
			{
				continue;
			}
					
			for(i=0;i<DATA_RECODER_MAX_ITEM;i++)
			{
				W25X_Read_Addr(faddr+i*32,pFileHeadBuf,32);
				Temp32 = *(uint32_t*)&pFileHeadBuf[0];
				Temp32 = Temp32&0x00FFFFFF;
				if((Temp32 != i))
				{
					break;
				}
			}	
			
			memset(pFileHeadBuf,0,128);
							
			if(speed == 0)
			{		
				End_Date = Start_Date + ((i/item_base)*0.05);
			}
			else
			{
				End_Date = Start_Date + ((i/item_base)*speed);
			}		

			*(uint32_t*)&pFileHeadBuf[0] = *(uint32_t*)"VTRE";
			*(uint32_t*)&pFileHeadBuf[4] = i;
			*(uint32_t*)&pFileHeadBuf[8] = End_Date;//file_head->End_Date;
			//*(uint32_t*)&pBuf[12] = file_head->End_Pos;
			*(uint32_t*)&pFileHeadBuf[16] = 6;//file_head->EndStatus;
			//*(uint32_t*)&pBuf[20] = 0;
			crc16 = CRC16Software(pFileHeadBuf,126);
			*(uint16_t*)&pFileHeadBuf[126] = crc16;

			faddr = id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR+128;
					
			W25X_WriteSector_Addr(faddr,pFileHeadBuf,128);
			
			file_info_simple[id].Used = 3;
			file_info_simple[id].id = id;
			file_info_simple[id].group = Group;
			file_info_simple[id].Start_Date = Start_Date;
		}
		else
		{
			Temp32 =  (pFileHeadBuf[128+4]<<0)|(pFileHeadBuf[128+5]<<8)|(pFileHeadBuf[128+6]<<16)|(pFileHeadBuf[128+7]<<24);
			if(Temp32 > (DATA_RECODER_MAX_ITEM + 20))
			{
				continue;
			}
		
			file_info_simple[id].Used = 3;
			file_info_simple[id].id = id;
			file_info_simple[id].group = Group;
			file_info_simple[id].Start_Date = Start_Date;      
		}
	}
}
//

/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
void Mem_FileHead_ReInit(void)
{
	uint16_t id;
	
	for(id=0;id<DATA_RECODER_TOTAL_ITEM;id++)
	{	
		W25X_Erase_Sector(id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR);
	}
	Mem_FileHead_Init();
}
//


/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
file_info_t *Mem_File_id_to_head(uint16_t id)
{
	if(id>DATA_RECODER_TOTAL_ITEM)
	{
		return 0;
	}
	
	return 0;//&file_info_buffer[id];

}


/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
int16_t read_file_info_nocheck(uint16_t id,file_info_t *FileInfo)
{
	uint32_t Temp32;
	uint16_t Group;
	uint8_t *FileHeadBuf;
	FileHeadBuf = flash_global_buf;
	
	if(id>DATA_RECODER_TOTAL_ITEM)
	{
		return -1;
	}
	
	MEM_LOCK();
	
	W25X_Read_Addr(id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR,FileHeadBuf,256);
	
	MEM_UNLOCK();
	
	Group = FileHeadBuf[4];
		
	FileInfo->file_id = id;
	FileInfo->group = Group;
	FileInfo->Used = 3;
	
	Temp32 = (FileHeadBuf[8]<<0)|(FileHeadBuf[9]<<8)|(FileHeadBuf[10]<<16)|(FileHeadBuf[11]<<24);
	FileInfo->Start_Date = Temp32;
	
	Temp32 = (FileHeadBuf[128+8]<<0)|(FileHeadBuf[128+9]<<8)|(FileHeadBuf[128+10]<<16)|(FileHeadBuf[128+11]<<24);
	FileInfo->End_Date = Temp32;
	
	Temp32 = (FileHeadBuf[20]<<0)|(FileHeadBuf[21]<<8)|(FileHeadBuf[22]<<16)|(FileHeadBuf[23]<<24);
	FileInfo->StartPos = id*DATA_RECODER_SIZE + DATA_RECODER_START_ADDR;
	
	Temp32 = (FileHeadBuf[128+12]<<0)|(FileHeadBuf[128+13]<<8)|(FileHeadBuf[128+14]<<16)|(FileHeadBuf[128+15]<<24);
	FileInfo->End_Pos = Temp32;
	
	Temp32 = (FileHeadBuf[128+4]<<0)|(FileHeadBuf[128+5]<<8)|(FileHeadBuf[128+6]<<16)|(FileHeadBuf[128+7]<<24);
	FileInfo->TotalItem = Temp32;
	
	Temp32 = (FileHeadBuf[16]<<0)|(FileHeadBuf[17]<<8);
	FileInfo->StartStatus = Temp32;
	
	Temp32 = (FileHeadBuf[128+16]<<0)|(FileHeadBuf[128+17]<<8);
	FileInfo->EndStatus = Temp32;
	
	Temp32 = (FileHeadBuf[18]<<0)|(FileHeadBuf[19]<<8);
	FileInfo->Meas_head.SamplingSpan = Temp32;
	
	Temp32 = (FileHeadBuf[74]<<0)|(FileHeadBuf[75]<<8);
	FileInfo->TagID = Temp32;

	Temp32 = (FileHeadBuf[24]<<0)|(FileHeadBuf[25]<<8)|(FileHeadBuf[26]<<16)|(FileHeadBuf[27]<<24);
	FileInfo->Meas_head.Start_Cond = Temp32;                               
																		 
	Temp32 = (FileHeadBuf[28]<<0)|(FileHeadBuf[29]<<8)|(FileHeadBuf[30]<<16)|(FileHeadBuf[31]<<24);
	FileInfo->Meas_head.Start_Params_1 = Temp32;                           
																		 
	Temp32 = (FileHeadBuf[32]<<0)|(FileHeadBuf[33]<<8)|(FileHeadBuf[34]<<16)|(FileHeadBuf[35]<<24);
	FileInfo->Meas_head.Start_Params_2 = Temp32;                           
																		 
	Temp32 = (FileHeadBuf[36]<<0)|(FileHeadBuf[37]<<8)|(FileHeadBuf[38]<<16)|(FileHeadBuf[39]<<24);
	FileInfo->Meas_head.Start_Params_3 = Temp32;                           
																		 
	Temp32 = (FileHeadBuf[40]<<0)|(FileHeadBuf[41]<<8)|(FileHeadBuf[42]<<16)|(FileHeadBuf[43]<<24);
	FileInfo->Meas_head.Start_Params_4 = Temp32;                           
																		 
	//-----------------------------------------
	
	Temp32 = (FileHeadBuf[44]<<0)|(FileHeadBuf[45]<<8)|(FileHeadBuf[46]<<16)|(FileHeadBuf[47]<<24);
	FileInfo->Meas_head.Stop_Cond = Temp32;                                
																		 
	Temp32 = (FileHeadBuf[48]<<0)|(FileHeadBuf[49]<<8)|(FileHeadBuf[50]<<16)|(FileHeadBuf[51]<<24);
	FileInfo->Meas_head.Stop_Params_1 = Temp32;                           
																		 
	Temp32 = (FileHeadBuf[52]<<0)|(FileHeadBuf[53]<<8)|(FileHeadBuf[54]<<16)|(FileHeadBuf[55]<<24);
	FileInfo->Meas_head.Stop_Params_2 = Temp32;                           
																		 
	Temp32 = (FileHeadBuf[56]<<0)|(FileHeadBuf[57]<<8)|(FileHeadBuf[58]<<16)|(FileHeadBuf[59]<<24);
	FileInfo->Meas_head.Stop_Params_3 = Temp32;                           
																		 
	Temp32 = (FileHeadBuf[60]<<0)|(FileHeadBuf[61]<<8)|(FileHeadBuf[62]<<16)|(FileHeadBuf[63]<<24);
	FileInfo->Meas_head.Stop_Params_4 = Temp32;
	
	return 0;
}
///*****************************************************************************************
//* Function Name  : Mem_File_Create_Test_Recoder
//* Description    : 
//* Input          : None
//* Output         : None
//* Return         : None
//*****************************************************************************************/
//void Mem_File_Create_Test_Recoder(void)
//{
//	uint16_t id,earliest_id;
//	uint16_t crc16;
//	uint32_t i;
//	uint8_t  *pBuf;
//	uint32_t recoder_date_earliest;
//	uint32_t SectorAddr;
//	file_info_t *pfileInfo_temp;
//	uint16_t loop_group,loop_file,loop_item;
//	
//	uint8_t test_mem_list[10]={30,10,2,3,4,1,1,2,1,5};
//	
//	uint8_t *FileHeadBuf;
//	
//	uint32_t file_total_count;
//		
//	uint32_t addr;
//	file_info_t *file_head;
//	
//	FileHeadBuf = flash_global_buf;
//	
//	file_total_count = 0;
//	for(loop_group=0;loop_group<10;loop_group++)
//	{
//		for(loop_file=0;loop_file<test_mem_list[loop_group];loop_file++)
//		{
//			file_total_count++;
//			
//			WWDG_SetCounter(0);
//			
//			recoder_date_earliest = 0xFFFFFFFF;
//			// an_modify
//			earliest_id = 0xffff;
//			for(id=0; id<DATA_RECODER_TOTAL_ITEM; id++)
//			{
//				if(file_info_simple[id].Used == 0)
//				{
//					break;
//				}
//				
//				if(file_info_simple[id].Start_Date < recoder_date_earliest)
//				{
//					recoder_date_earliest = file_info_simple[id].Start_Date;
//					earliest_id =id;			
//				}
//			}
//			
//			if(id>=DATA_RECODER_TOTAL_ITEM) 
//				id = earliest_id;
//			
//			read_file_info_nocheck(id,&file_info_buffer_new);
//			pfileInfo_temp = &file_info_buffer_new;
//			pfileInfo_temp->file_id = id;
//			
//			pfileInfo_temp->Meas_head.SampleInterval = 0;
//			pfileInfo_temp->Meas_head.group = loop_group;
//			
//			pfileInfo_temp->Meas_head.TagID = 0;
//			
//			pfileInfo_temp->StartStatus = 0;
//			pfileInfo_temp->StartPos = pfileInfo_temp->Addr;
//			pfileInfo_temp->Start_Date = iProfile.sys_status.Date.UTC_Date + file_total_count*3600;
//			
//			pfileInfo_temp->EndStatus = 0;
//			pfileInfo_temp->End_Date = 0;
//			pfileInfo_temp->End_Pos = 0;
//			pfileInfo_temp->TotalItem = 0;
//			
//			pfileInfo_temp->Meas_head.Start_Cond = 0;
//			pfileInfo_temp->Meas_head.Start_Params_1 = 0;
//			pfileInfo_temp->Meas_head.Start_Params_2 = 0;
//			pfileInfo_temp->Meas_head.Start_Params_3 = 0;
//			pfileInfo_temp->Meas_head.Start_Params_4 = 0;
//			
//			pfileInfo_temp->Meas_head.Stop_Cond = 0;
//			pfileInfo_temp->Meas_head.Stop_Params_1 = 0;
//			pfileInfo_temp->Meas_head.Stop_Params_2 = 0;
//			pfileInfo_temp->Meas_head.Stop_Params_3 = 0;
//			pfileInfo_temp->Meas_head.Stop_Params_4 = 0;
//		  
//			SectorAddr  = pfileInfo_temp->Addr;
//			
//			W25X_Erase_Sector(SectorAddr);

//			
//			for(i=0;i<128;i++)
//			{
//				FileHeadBuf[i] = 0xFF;
//			}
//			
//			i = 0;
//			pBuf = FileHeadBuf;

//			*(uint32_t*)&pBuf[0] = *(uint32_t*)"VTRH";	
//			pBuf[4] = pfileInfo_temp->Meas_head.group;  
//			pBuf[5] = 0;
//			pBuf[6] = RECODER_FILE_VERSION;
//			pBuf[7] = DEVICE_TYPE;
//			*(uint32_t*)&pBuf[8] = pfileInfo_temp->Start_Date;	
//			pBuf[12] = DEV_TYPE;
//			pBuf[13] = iProfile.conf.dev_ext_type;
//			pBuf[14] = iProfile.conf.Total_channel;
//			pBuf[15] = iProfile.conf.in_group_item_cnt;
//			*(uint16_t*)&pBuf[16] = pfileInfo_temp->StartStatus;
//			*(uint16_t*)&pBuf[18] = pfileInfo_temp->Meas_head.SampleInterval;
//			*(uint32_t*)&pBuf[20] = pfileInfo_temp->StartPos;
//			
//			*(uint32_t*)&pBuf[24] = pfileInfo_temp->Meas_head.Start_Cond;
//			*(uint32_t*)&pBuf[28] = pfileInfo_temp->Meas_head.Start_Params_1;
//			*(uint32_t*)&pBuf[32] = pfileInfo_temp->Meas_head.Start_Params_2;
//			*(uint32_t*)&pBuf[36] = pfileInfo_temp->Meas_head.Start_Params_3;
//			*(uint32_t*)&pBuf[40] = pfileInfo_temp->Meas_head.Start_Params_4;

//			*(uint32_t*)&pBuf[44] = pfileInfo_temp->Meas_head.Stop_Cond;
//			*(uint32_t*)&pBuf[48] = pfileInfo_temp->Meas_head.Stop_Params_1;
//			*(uint32_t*)&pBuf[52] = pfileInfo_temp->Meas_head.Stop_Params_2;
//			*(uint32_t*)&pBuf[56] = pfileInfo_temp->Meas_head.Stop_Params_3;
//			*(uint32_t*)&pBuf[60] = pfileInfo_temp->Meas_head.Stop_Params_4;
//			
//			pBuf[68] = DEV_UID[1];
//			pBuf[69] = DEV_UID[3];
//			pBuf[70] = DEV_UID[5];
//			pBuf[71] = DEV_UID[2];
//			pBuf[72] = DEV_UID[4];
//			pBuf[73] = DEV_UID[6];
//			
//			pBuf[74] = (pfileInfo_temp->Meas_head.TagID>>0)&0xFF;
//			pBuf[75] = (pfileInfo_temp->Meas_head.TagID>>8)&0xFF;
//					
//			pBuf[76] = V_DATA_CNT;
//			
//			for(i=0;i<V_DATA_CNT;i++)
//			{
//				pBuf[77 + i] = v_data_table[i];
//			}
//			for(;i<32;i++)
//			{
//				pBuf[77 + i] = 0;
//			}
//				
//			crc16 = CRC16Software(FileHeadBuf,126);
//			
//			pBuf = &FileHeadBuf[126];
//			*(uint16_t*)pBuf = crc16;
//			
//			W25X_WriteSector_Addr(SectorAddr,FileHeadBuf,128);
//			//W25X_Read_Addr(SectorAddr,FileHeadBuf,256);
//			pfileInfo_temp->Write_Addr = SectorAddr + 128 + 128;
//			
//			pfileInfo_temp->Used = 2;
//						
//			WWDG_SetCounter(0);
//			//---------------------------------------------------------------------------------------------------------------
//			FileHeadBuf = flash_global_buf;
//			
//			file_head = pfileInfo_temp;
//			
//			for(loop_item = 0; loop_item < 50; loop_item++)
//			{
//				WWDG_SetCounter(0);
//				Mem_File_WriteItem(file_head,0,0,0,(int16_t *)flash_global_buf);
//			}

//			W25X_Wait_Busy();

//			file_head->Used = 1;

//			file_head->EndStatus = 0;
//			file_head->End_Date = iProfile.sys_status.Date.UTC_Date + file_total_count*3600 + 50;	
//			file_head->End_Pos = file_head->Write_Addr;
//			//file_head->TotalItem;

//			addr = file_head->Addr;

//			W25X_Read_Addr(addr,FileHeadBuf,8);
//			addr += 128;

//			//W25X_Read_Addr(addr,FileHeadBuf,128);

//			for(i=0;i<128;i++)
//			{
//				FileHeadBuf[i] = 0xFF;
//			}

//			i = 0;
//			pBuf = FileHeadBuf;

//			*(uint32_t*)&pBuf[0] = *(uint32_t*)"VTRE";
//			*(uint32_t*)&pBuf[4] = file_head->TotalItem;
//			*(uint32_t*)&pBuf[8] = file_head->End_Date;
//			//*(uint32_t*)&pBuf[12] = file_head->End_Pos;
//			*(uint16_t*)&pBuf[16] = file_head->EndStatus;
//			//*(uint32_t*)&pBuf[20] = 0;

//			crc16 = CRC16Software(FileHeadBuf,126);
//			pBuf = &FileHeadBuf[126];
//			*(uint16_t*)pBuf = crc16;

//			WWDG_SetCounter(0);
//			
//			W25X_WriteSector_Addr(addr,FileHeadBuf,128);

//			/* an_add */
//			file_info_simple[file_head->file_id].Used = 3;
//			file_info_simple[file_head->file_id].id = file_head->file_id;
//			file_info_simple[file_head->file_id].group = file_head->Meas_head.group;
//			file_info_simple[file_head->file_id].Start_Date = file_head->Start_Date;

//			file_head->Used = 3;	
//			file_head = RT_NULL;
//					
//		}
//	}
//	MEM_UNLOCK();	
//}
////

uint16_t recode_cnt_test;
file_info_t *Mem_File_CreateRecoder(uint16_t group,uint16_t tag_id,uint32_t start_date,uint16_t start_status,MEAS_PARAMS *pmeas_params)
{
	uint16_t id,earliest_id;
	uint16_t crc16;
	uint32_t i;
    
	uint8_t  *pBuf;
    
    uint8_t total_write_item_cnt;
    
	uint32_t recoder_date_earliest;
	uint32_t SectorAddr;
	file_info_t *pfileInfo_temp;
	
	uint8_t *FileHeadBuf;
	FileHeadBuf = flash_global_buf;
	
	recode_cnt_test = 0;
	total_write_item_cnt = 0;
	MEM_LOCK();
    
#if (USE_TEMPERATURE != 0)    
    total_write_item_cnt++;
    if(iProfile.conf.Total_channel > 12)
    {
        total_write_item_cnt++;
    }    
    if(iProfile.conf.Total_channel > 24)
    {
        total_write_item_cnt++;
    }
#endif
#if (USE_VIBRATE != 0)  
    total_write_item_cnt += 1;
#endif
	
	recoder_date_earliest = 0xFFFFFFFF;

	// an_modify
	earliest_id = 0xffff;
	for(id=0; id<DATA_RECODER_TOTAL_ITEM; id++)
	{
		if(file_info_simple[id].Used == 0)
		{
			break;
		}
		
		if(file_info_simple[id].Start_Date < recoder_date_earliest)
		{
			recoder_date_earliest = file_info_simple[id].Start_Date;
			earliest_id =id;			
		}
	}
	
	if(id==DATA_RECODER_TOTAL_ITEM) 
        id = earliest_id;
	
	read_file_info_nocheck(id,&file_info_buffer_new);
	pfileInfo_temp = &file_info_buffer_new;
	pfileInfo_temp->file_id = id;
	pfileInfo_temp->group = group;
	pfileInfo_temp->StartPos = DATA_RECODER_SIZE * id + DATA_RECODER_START_ADDR;
	pfileInfo_temp->Start_Date = start_date;
	pfileInfo_temp->EndStatus = 0;
	pfileInfo_temp->End_Date = 0;
	pfileInfo_temp->End_Pos = 0;
	pfileInfo_temp->TotalItem = 0;

	pfileInfo_temp->TagID = 				tag_id;
	
	pfileInfo_temp->StartStatus = start_status;
	
	pfileInfo_temp->Meas_head.SamplingSpan      = pmeas_params->SamplingSpan;
	
	pfileInfo_temp->Meas_head.Start_Cond        = pmeas_params->Start_Cond;
	pfileInfo_temp->Meas_head.Start_Params_1    = pmeas_params->Start_Params_1;
	pfileInfo_temp->Meas_head.Start_Params_2    = pmeas_params->Start_Params_2;
	pfileInfo_temp->Meas_head.Start_Params_3    = pmeas_params->Start_Params_3;
	pfileInfo_temp->Meas_head.Start_Params_4    = pmeas_params->Start_Params_4;
	
	pfileInfo_temp->Meas_head.Stop_Cond         = pmeas_params->Stop_Cond;
	pfileInfo_temp->Meas_head.Stop_Params_1     = pmeas_params->Stop_Params_1;
	pfileInfo_temp->Meas_head.Stop_Params_2     = pmeas_params->Stop_Params_2;
	pfileInfo_temp->Meas_head.Stop_Params_3     = pmeas_params->Stop_Params_3;
	pfileInfo_temp->Meas_head.Stop_Params_4     = pmeas_params->Stop_Params_4;
  
	SectorAddr  = pfileInfo_temp->StartPos;
	
	W25X_Erase_Sector(SectorAddr);

	for(i=0;i<128;i++)
	{
		FileHeadBuf[i] = 0xFF;
	}
	
	i = 0;
	pBuf = FileHeadBuf;

	*(uint32_t*)&pBuf[0] = *(uint32_t*)"VTRH";	
	pBuf[4] = pfileInfo_temp->group;
	pBuf[5] = 0;
	pBuf[6] = RECODER_FILE_VERSION;
	pBuf[7] = DEVICE_TYPE;
	*(uint32_t*)&pBuf[8] = pfileInfo_temp->Start_Date;	
	pBuf[12] = DEV_TYPE;
	pBuf[13] = iProfile.conf.dev_ext_type;
	pBuf[14] = iProfile.conf.Total_channel;
	pBuf[15] = total_write_item_cnt;
	*(uint16_t*)&pBuf[16] = pfileInfo_temp->StartStatus;
	*(uint16_t*)&pBuf[18] = pfileInfo_temp->Meas_head.SamplingSpan;
	*(uint32_t*)&pBuf[20] = pfileInfo_temp->StartPos;
	
	*(uint32_t*)&pBuf[24] = pfileInfo_temp->Meas_head.Start_Cond;
	*(uint32_t*)&pBuf[28] = pfileInfo_temp->Meas_head.Start_Params_1;
	*(uint32_t*)&pBuf[32] = pfileInfo_temp->Meas_head.Start_Params_2;
	*(uint32_t*)&pBuf[36] = pfileInfo_temp->Meas_head.Start_Params_3;
	*(uint32_t*)&pBuf[40] = pfileInfo_temp->Meas_head.Start_Params_4;

	*(uint32_t*)&pBuf[44] = pfileInfo_temp->Meas_head.Stop_Cond;
	*(uint32_t*)&pBuf[48] = pfileInfo_temp->Meas_head.Stop_Params_1;
	*(uint32_t*)&pBuf[52] = pfileInfo_temp->Meas_head.Stop_Params_2;
	*(uint32_t*)&pBuf[56] = pfileInfo_temp->Meas_head.Stop_Params_3;
	*(uint32_t*)&pBuf[60] = pfileInfo_temp->Meas_head.Stop_Params_4;
	
	pBuf[68] = DEV_UID[1];
	pBuf[69] = DEV_UID[3];
	pBuf[70] = DEV_UID[5];
	pBuf[71] = DEV_UID[2];
	pBuf[72] = DEV_UID[4];
	pBuf[73] = DEV_UID[6];
	
	pBuf[74] = (pfileInfo_temp->TagID>>0)&0xFF;
	pBuf[75] = (pfileInfo_temp->TagID>>8)&0xFF;
			
	pBuf[76] = V_DATA_CNT;
	
	for(i=0;i<V_DATA_CNT + 1;i++)
	{
		pBuf[77 + i] = v_data_table[i];
	}
    
    pBuf[109] = USE_VIBRATE;
    
    pBuf[120] = 0;
	pBuf[121] = 0;

	pBuf[122] = iProfile.conf.version_Major;
	pBuf[123] = iProfile.conf.version_minor;
	pBuf[124] = iProfile.conf.version_revison;
	pBuf[125] = iProfile.conf.version_label;
		
	crc16 = CRC16Software(FileHeadBuf,126);
	
	pBuf = &FileHeadBuf[126];
	*(uint16_t*)pBuf = crc16;
	
	W25X_WriteSector_Addr(SectorAddr,FileHeadBuf,128);
	//W25X_Read_Addr(SectorAddr,FileHeadBuf,256);
	pfileInfo_temp->Write_Addr = SectorAddr + 128 + 128;
	
	pfileInfo_temp->Used = 2;
	
	MEM_UNLOCK();
	
	return pfileInfo_temp;
}
//

/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
int32_t Mem_File_CloseRecoder(file_info_t *file_head,uint16_t end_status)
{
	int8_t status;
	uint8_t  *pBuf;
	uint16_t i;
	uint32_t addr;
	uint16_t Group;
	uint16_t crc16;
	
	uint8_t *FileHeadBuf;
	FileHeadBuf = flash_global_buf;
	
	status = 0;
	
	MEM_LOCK();
	
	if(file_head==NULL)
	{
		status =  -1;
		goto end;
	}
	
	if(file_head->Used !=2)
	{
		status =  -1;
		goto end;
	}
	
	W25X_Wait_Busy();
	
	file_head->Used = 1;
		
	file_head->EndStatus = end_status;
	file_head->End_Date = iProfile.sys_status.Date.UTC_Date;	
	file_head->End_Pos = file_head->Write_Addr;
	//file_head->TotalItem;
	
	addr = file_head->StartPos;
	
	W25X_Read_Addr(addr,FileHeadBuf,8);
	
	Group = FileHeadBuf[4];
	
	if(file_head->group!=Group)
	{
		status =  -1;
		goto end;
	}
	
	addr += 128;
	
	//W25X_Read_Addr(addr,FileHeadBuf,128);
	
	for(i=0;i<128;i++)
	{
		FileHeadBuf[i] = 0xFF;
	}
	
	i = 0;
	pBuf = FileHeadBuf;

	*(uint32_t*)&pBuf[0] = *(uint32_t*)"VTRE";
	*(uint32_t*)&pBuf[4] = file_head->TotalItem;
	*(uint32_t*)&pBuf[8] = file_head->End_Date;
	//*(uint32_t*)&pBuf[12] = file_head->End_Pos;
	*(uint16_t*)&pBuf[16] = file_head->EndStatus;
	//*(uint32_t*)&pBuf[20] = 0;
	
	crc16 = CRC16Software(FileHeadBuf,126);
	pBuf = &FileHeadBuf[126];
	*(uint16_t*)pBuf = crc16;
	
	W25X_WriteSector_Addr(addr,FileHeadBuf,128);
    
	/* an_add */
	file_info_simple[file_head->file_id].Used = 3;
	file_info_simple[file_head->file_id].id = file_head->file_id;
	file_info_simple[file_head->file_id].group = file_head->group;
	file_info_simple[file_head->file_id].Start_Date = file_head->Start_Date;
	
	file_head->Used = 3;	
	file_head = NULL;
	
end:
	MEM_UNLOCK();
	
	return status;
}
//

/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
static uint8_t write_item_buftmp[40];
int32_t Mem_File_WriteItem(file_info_t *file_head,uint8_t res)
{
	uint32_t i,j;
	uint32_t addr;
    uint8_t temp_channel;
    uint32_t temp_32;
	uint8_t  temp_8;
	int16_t *pbuf;
    
    uint16_t write_iteam;
    
    write_iteam = 0;
    
	if(file_head->Used !=2)
	{
		return -1;
	}
    	
	MEM_LOCK();
            
#if (USE_TEMPERATURE != 0)          
    temp_channel = iProfile.conf.Total_channel;
    for(i=0;i<3;i++)
    {    
		temp_8 = v_data_table[(file_head->v_data_point)];
		write_item_buftmp[0] = (file_head->TotalItem>>0)&0xFF;
		write_item_buftmp[1] = (file_head->TotalItem>>8)&0xFF;
		write_item_buftmp[2] = (file_head->TotalItem>>16)&0xFF;
		write_item_buftmp[3] = (i<<4)|temp_8;

		temp_32 = iProfile.sys_status.v_data_buf[temp_8];
		write_item_buftmp[4] = (temp_32>>0)&0xFF;
		write_item_buftmp[5] = (temp_32>>8)&0xFF;
		write_item_buftmp[6] = (temp_32>>16)&0xFF;
		write_item_buftmp[7] = (temp_32>>24)&0xFF;

		file_head->TotalItem++;
		file_head->v_data_point++;

		if(file_head->v_data_point >= V_DATA_CNT)
		{
			file_head->v_data_point = 0;
		}
			
		pbuf = (int16_t*)&write_item_buftmp[8]; 


		for(j=0;j<12;j++)
		{
            if(j<(temp_channel-(i*12)))
            {
                pbuf[j] = 0xa5; //iProfile.temperature.temperature[i*12 + j];
            }
            else
            {
                pbuf[j] = -999;
            }			
		}
					 
		addr = file_head->Write_Addr;

		W25X_Wait_Busy();
		W25X_Write_Enable();
		W25X_CS_L();

		SPI_Send_Byte(W25X_PageProgram);
		// SPI_Send_Byte(W25X_PageProgram4);
		// SPI_Send_Byte((uint8_t)(addr >> 24));
		SPI_Send_Byte((uint8_t)(addr >> 16));
		SPI_Send_Byte((uint8_t)(addr >> 8));
		SPI_Send_Byte((uint8_t)(addr >> 0));

		for(j=0;j<32;j++)
		{
			SPI_Send_Byte(write_item_buftmp[j]);
		}

		W25X_CS_H();

		file_head->Write_Addr = addr + 32;
        
        write_iteam++;

		if(file_head->TotalItem>DATA_RECODER_MAX_ITEM)	//h_start+hstop+data
		{
			goto error;
		}

		if((file_head->Write_Addr%4096)==0)
		{
			addr = file_head->Write_Addr + 4096;
			write_item_buftmp[0] = 0;
			write_item_buftmp[1] = 0;	
			write_item_buftmp[2] = 0;
			write_item_buftmp[3] = 0;

			W25X_Wait_Busy();
			W25X_Write_Enable();

			W25X_CS_L();


			SPI_Send_Byte(W25X_PageProgram);
			// SPI_Send_Byte(W25X_PageProgram4);
			// SPI_Send_Byte((uint8_t)(addr >> 24));
			SPI_Send_Byte((uint8_t)(addr >> 16));
			SPI_Send_Byte((uint8_t)(addr >> 8));
			SPI_Send_Byte((uint8_t)(addr >> 0));

			for(j=0;j<4;j++)
			{
				SPI_Send_Byte(write_item_buftmp[j]);
			}
			W25X_CS_H();

			W25X_Erase_Sector(file_head->Write_Addr);	
		}
		
		if((i+1)*12 >= temp_channel)
		{
			break;
		}			
    }
#endif
    
//#if ((USE_VIBRATE != 0))
    
    temp_8 = v_data_table[(file_head->v_data_point)];
    write_item_buftmp[0] = (file_head->TotalItem>>0)&0xFF;
    write_item_buftmp[1] = (file_head->TotalItem>>8)&0xFF;
    write_item_buftmp[2] = (file_head->TotalItem>>16)&0xFF;
    write_item_buftmp[3] = (6<<4)|temp_8;

    temp_32 = iProfile.sys_status.v_data_buf[temp_8];
    write_item_buftmp[4] = (temp_32>>0)&0xFF;
    write_item_buftmp[5] = (temp_32>>8)&0xFF;
    write_item_buftmp[6] = (temp_32>>16)&0xFF;
    write_item_buftmp[7] = (temp_32>>24)&0xFF;

    file_head->TotalItem++;
    file_head->v_data_point++;

    if(file_head->v_data_point >= V_DATA_CNT)
    {
        file_head->v_data_point = 0;
    }
        
    pbuf = (int16_t*)&write_item_buftmp[8]; 

    pbuf[0]  = 0xa5;	//iProfile.pressure.pressure_data;
    pbuf[1]  = 0xa5;	//(iProfile.oxygen.oxygen_value>>0)&0xFFFF;
    pbuf[2]  = 0xa5;	//(iProfile.oxygen.oxygen_value>>16)&0xFFFF;
    pbuf[3]  = 0xa5;	//iProfile.vibrate.vib_x;
    pbuf[4]  = 0xa5;	//iProfile.vibrate.vib_y;
    pbuf[5]  = 0xa5;	//iProfile.vibrate.vib_z;
    pbuf[6]  = 0xa5;	//iProfile.r_width.relative;
    pbuf[7]  = 0;
    pbuf[8]  = 0xa5;	//iProfile.vibrate.roll;
    pbuf[9]  = 0xa5;	//iProfile.vibrate.pitch;
    pbuf[10] = 0;
    pbuf[11] = 0;
					 
    addr = file_head->Write_Addr;

    W25X_Wait_Busy();
    W25X_Write_Enable();
    W25X_CS_L();

    SPI_Send_Byte(W25X_PageProgram);
    // SPI_Send_Byte(W25X_PageProgram4);
    // SPI_Send_Byte((uint8_t)(addr >> 24));
    SPI_Send_Byte((uint8_t)(addr >> 16));
    SPI_Send_Byte((uint8_t)(addr >> 8));
    SPI_Send_Byte((uint8_t)(addr >> 0));

    for(j=0;j<32;j++)
    {
        SPI_Send_Byte(write_item_buftmp[j]);
    }

    W25X_CS_H();

    file_head->Write_Addr = addr + 32;
    
    write_iteam++;

    if(file_head->TotalItem>DATA_RECODER_MAX_ITEM)	//h_start+hstop+data
    {
        goto error;
    }

    if((file_head->Write_Addr%4096)==0)
    {
        addr = file_head->Write_Addr + 4096;
        write_item_buftmp[0] = 0;
        write_item_buftmp[1] = 0;	
        write_item_buftmp[2] = 0;
        write_item_buftmp[3] = 0;

        W25X_Wait_Busy();
        W25X_Write_Enable();

        W25X_CS_L();
        
        SPI_Send_Byte(W25X_PageProgram);
        // SPI_Send_Byte(W25X_PageProgram4);
        // SPI_Send_Byte((uint8_t)(addr >> 24));
        SPI_Send_Byte((uint8_t)(addr >> 16));
        SPI_Send_Byte((uint8_t)(addr >> 8));
        SPI_Send_Byte((uint8_t)(addr >> 0));

        for(j=0;j<4;j++)
        {
            SPI_Send_Byte(write_item_buftmp[j]);
        }
        W25X_CS_H();

        W25X_Erase_Sector(file_head->Write_Addr);	
    }
//#endif

#if ((USE_VIBRATE != 0)&&(USE_VIBRATE_REALTIME!=0))          

    for(i=0;i<3;i++)
    {    
		temp_8 = v_data_table[(file_head->v_data_point)];
        write_item_buftmp[0] = (file_head->TotalItem>>0)&0xFF;
        write_item_buftmp[1] = (file_head->TotalItem>>8)&0xFF;
        write_item_buftmp[2] = (file_head->TotalItem>>16)&0xFF;
        write_item_buftmp[3] = ((8+i)<<4)|temp_8;
        
        temp_32 = iProfile.sys_status.v_data_buf[temp_8];
        write_item_buftmp[4] = (temp_32>>0)&0xFF;
        write_item_buftmp[5] = (temp_32>>8)&0xFF;
        write_item_buftmp[6] = (temp_32>>16)&0xFF;
        write_item_buftmp[7] = (temp_32>>24)&0xFF;
        
        file_head->TotalItem++;
        file_head->v_data_point++;
        
        if(file_head->v_data_point >= V_DATA_CNT)
        {
            file_head->v_data_point = 0;
        }
            
        pbuf = (int16_t*)&write_item_buftmp[8];       
        if(i==0)
        {
            for(j=0;j<10;j++)
            {            
                pbuf[j] = iProfile.vibrate.x[j];
            }
        }
        else if(i == 1)
        {
            for(j=0;j<10;j++)
            {            
                pbuf[j] = iProfile.vibrate.y[j];
            }
        }
        else if(i == 2)
        {
            for(j=0;j<10;j++)
            {            
                pbuf[j] = iProfile.vibrate.z[j];
            }
        }
                            
        addr = file_head->Write_Addr;
        
        W25X_Wait_Busy();
        W25X_Write_Enable();
        W25X_CS_L();

        SPI_Send_Byte(W25X_PageProgram4);
        SPI_Send_Byte((uint8_t)(addr >> 24));
        SPI_Send_Byte((uint8_t)(addr >> 16));
        SPI_Send_Byte((uint8_t)(addr >> 8));
        SPI_Send_Byte((uint8_t)(addr >> 0));

        for(j=0;j<32;j++)
        {
            SPI_Send_Byte(write_item_buftmp[j]);
        }

        W25X_CS_H();

        file_head->Write_Addr = addr + 32;

        if(file_head->TotalItem>DATA_RECODER_MAX_ITEM)	//h_start+hstop+data
        {
            goto error;
        }

        if((file_head->Write_Addr%4096)==0)
        {
            addr = file_head->Write_Addr + 4096;
            write_item_buftmp[0] = 0;
            write_item_buftmp[1] = 0;	
            write_item_buftmp[2] = 0;
            write_item_buftmp[3] = 0;

            W25X_Wait_Busy();
            W25X_Write_Enable();

            W25X_CS_L();

            SPI_Send_Byte(W25X_PageProgram4);
            SPI_Send_Byte((uint8_t)(addr >> 24));
            SPI_Send_Byte((uint8_t)(addr >> 16));
            SPI_Send_Byte((uint8_t)(addr >> 8));
            SPI_Send_Byte((uint8_t)(addr >> 0));

            for(j=0;j<4;j++)
            {
                SPI_Send_Byte(write_item_buftmp[j]);
            }
            W25X_CS_H();

            W25X_Erase_Sector(file_head->Write_Addr);	
       }                                        
    }
#endif
    MEM_UNLOCK();
    return write_iteam;
error: 
    MEM_UNLOCK();    
    return -2;
}


/*****************************************************************************************
* Function Name  : 
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
int32_t Mem_File_GetGroupFile(uint16_t group,int16_t file_id,file_info_t **file_head)
{
	uint16_t id;
	uint16_t Group_id;
	uint16_t file_counter;
	int32_t result;
	
//	if(file_head == NULL)
//	{
//		return -1;
//	}
	
	result = -1;
	file_counter = 0;
	
	for(id=0;id<DATA_RECODER_TOTAL_ITEM;id++)
	{
		if(file_info_simple[id].Used != 3)
		{
			continue;
		}
		
		Group_id = file_info_simple[id].group;
		
		if(Group_id != group) 
			continue;
		
		file_counter++;
		
		if(file_id == -1)
		{
			continue;
		}
			
		if(file_counter == (file_id+1))
		{
			//*file_head = &file_info_buffer[id];
			result = id;
			break;
		}			
	}
	
	if(file_id == -1)
	{
		result = file_counter;
	}
	return result;	
}
//

/*****************************************************************************************
* Function Name  : GetGroupFile_use
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
uint16_t GetGroupFile_use(void)
{
	uint16_t file_counter;
	uint16_t id;
  
	file_counter = 0;
	
	for(id=0;id<DATA_RECODER_TOTAL_ITEM;id++)
	{
		if(file_info_simple[id].Used != 3)
		{
			continue;
		}
    
		file_counter++;	
	}
	
	return file_counter;	
}

/*****************************************************************************************
* Function Name  : Mem_File_ReadFileInit
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
int32_t Mem_File_ReadFileInit(file_info_t *file_head)
{
//	uint16_t id;
//	uint16_t Group_id;
//	uint16_t file_counter;
//	uint32_t Temp32;
	int32_t result;
	
	if(file_head == NULL)
	{
		return -1;
	}
	
	if(file_head->Used!=3)
	{
		return -1;
	}
	
	file_head->Current_addr = file_head->StartPos;
	result = 0;
	
	return result;	
}
//


/*****************************************************************************************
* Function Name  : Mem_File_ReadFile
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
int32_t Mem_File_ReadFile(file_info_t *file_head,int offset,int ReadLength,uint8_t *pbuf)
{
	int read_addr;
	int length;
	
	MEM_LOCK();
	
	read_addr = file_head->StartPos + offset;
	length = ReadLength;
//	if(read_addr>file_head->End_Pos)
//	{
//		for(length = 0;length<ReadLength;length++)
//		{
//			pbuf[length] = 0x00;
//		}
//		
//		length = 0;
//		goto end;
//	}
//	
//	if((read_addr+ReadLength)>file_head->End_Pos)
//	{
//		length = file_head->End_Pos - read_addr;
//	}
//	else
//	{
//		length = ReadLength;
//	}
	
	W25X_Read_Addr(read_addr,pbuf,length);
	file_head->Current_addr = file_head->Current_addr+length;
		
//end:
	MEM_UNLOCK();
	
	return length;	
}
//
/*****************************************************************************************
* Function Name  : fw_flash_write
* Description    : 
* Input          : None
* Output         : None
* Return         : None
*****************************************************************************************/
int fw_flash_write(uint32_t addr,uint8_t *buf,uint16_t size)
{
	int32_t write_addr;
	
	write_addr = addr + FW_MEM_ADDR;
	if(write_addr >= DATA_RECODER_START_ADDR)
	{
		return -1;
	}
	
	MEM_LOCK();
	
	if((write_addr%4096)==0)
	{
		W25X_Erase_Sector(write_addr);		
	}
	
	W25X_Write_Addr(write_addr,buf,size);

	MEM_UNLOCK();
			
	return 0;
}

int fw_flash_write_success(int fw_size)
{
	uint8_t temp_buf[8];
	
	W25X_Erase_Sector(NVM_8_ADDR_FW_UPDATE_FLAG);
	
	temp_buf[0] = 0xAA;
	temp_buf[1] = 0xCC;
	temp_buf[2] = 0xBB;
	temp_buf[3] = 0xDD;
	
	temp_buf[4] = (fw_size>>0)&0xFF;
	temp_buf[5] = (fw_size>>8)&0xFF;
	temp_buf[6] = (fw_size>>16)&0xFF;
	temp_buf[7] = (fw_size>>24)&0xFF;
	
	W25X_WriteSector_Addr(NVM_8_ADDR_FW_UPDATE_FLAG,temp_buf,8);	
	
	return 0;
}

int fw_flash_write_clean(void)
{
	uint8_t temp_buf[8];
	
	W25X_Erase_Sector(NVM_8_ADDR_FW_UPDATE_FLAG);
	
	temp_buf[0] = 0xFF;
	temp_buf[1] = 0xFF;
	temp_buf[2] = 0xFF;
	temp_buf[3] = 0xFF;
	
	temp_buf[4] = 0;
	temp_buf[5] = 0;
	temp_buf[6] = 0;
	temp_buf[7] = 0;
	
	
	W25X_WriteSector_Addr(NVM_8_ADDR_FW_UPDATE_FLAG,temp_buf,8);	
	
	return 0;
}


//----------------------------------------------------- ef_log ---------------------------------


/**
 * Read data from flash.
 * @note This operation's units is word.
 *
 * @param addr flash address
 * @param buf buffer to store read data
 * @param size read bytes size
 *
 * @return result
 */
EfErrCode ef_port_read(uint32_t addr, uint32_t *buf, size_t size) 
{
    EfErrCode result = EF_NO_ERR;
    MEM_LOCK();
    
    W25X_Read_Addr(addr,(uint8_t *)buf,size);

    MEM_UNLOCK();
    return result;
}

/**
 * Erase data on flash.
 * @note This operation is irreversible.
 * @note This operation's units is different which on many chips.
 *
 * @param addr flash address
 * @param size erase bytes size
 *
 * @return result
 */
EfErrCode ef_port_erase(uint32_t addr, size_t size)
{
    EfErrCode result = EF_NO_ERR;

    int erase_size;
    int erase_addr;
    
    MEM_LOCK();

    
    erase_size = size;
    erase_addr = addr;
    while(erase_size > 0)
    {
        WDG_Refresh();
        W25X_Erase_Sector(erase_addr);
        erase_addr += 4096;
        erase_size -= 4096;
    }
        
    MEM_UNLOCK();

    return result;
}
/**
 * Write data to flash.
 * @note This operation's units is word.
 * @note This operation must after erase. @see flash_erase.
 *
 * @param addr flash address
 * @param buf the write data buffer
 * @param size write bytes size
 *
 * @return result
 */
EfErrCode ef_port_write(uint32_t addr, const uint32_t *buf, size_t size) {
    EfErrCode result = EF_NO_ERR;

    MEM_LOCK();
    W25X_WriteSector_Addr(addr,(uint8_t *)buf,size);	
    MEM_UNLOCK();
    
    return result;
}



/* magic code on every sector header. 'EF' is 0xEF30EF30 */
#define LOG_SECTOR_MAGIC               0xEF30EF30
/* sector header size, includes the sector magic code and status magic code */
#define LOG_SECTOR_HEADER_SIZE         12
/* sector header word size,what is equivalent to the total number of sectors header index */
#define LOG_SECTOR_HEADER_WORD_SIZE    3

/**
 * Sector status magic code
 * The sector status is 8B after LOG_SECTOR_MAGIC at every sector header.
 * ==============================================
 * |           header(12B)            | status |
 * ----------------------------------------------
 * | 0xEF30EF30 0xFFFFFFFF 0xFFFFFFFF |  empty |
 * | 0xEF30EF30 0xFEFEFEFE 0xFFFFFFFF |  using |
 * | 0xEF30EF30 0xFEFEFEFE 0xFCFCFCFC |  full  |
 * ==============================================
 *
 * State transition relationship: empty->using->full
 * The FULL status will change to EMPTY after sector clean.
 */
#define SECTOR_STATUS_MAGIC_EMPUT     0xFFFFFFFF
#define SECTOR_STATUS_MAGIC_USING     0xFEFEFEFE
#define SECTOR_STATUS_MAGIC_FULL      0xFCFCFCFC

typedef enum {
    SECTOR_STATUS_EMPUT,
    SECTOR_STATUS_USING,
    SECTOR_STATUS_FULL,
    SECTOR_STATUS_HEADER_ERROR,
} SectorStatus;

typedef enum {
    SECTOR_HEADER_MAGIC_INDEX,
    SECTOR_HEADER_USING_INDEX,
    SECTOR_HEADER_FULL_INDEX,
} SectorHeaderIndex;

/* the stored logs start address and end address. It's like a ring buffer implemented on flash. */
static uint32_t log_start_addr = 0, log_end_addr = 0;
/* saved log area address for flash */
static uint32_t log_area_start_addr = 0;
/* initialize OK flag */
static uint8_t init_ok = 0;

static void find_start_and_end_addr(void);
static uint32_t get_next_flash_sec_addr(uint32_t cur_addr);

/**
 * The flash save log function initialize.
 *
 * @return result
 */
EfErrCode ef_log_init(void) 
{
    EfErrCode result = EF_NO_ERR;

    EF_ASSERT(LOG_AREA_SIZE);
    EF_ASSERT(EF_ERASE_MIN_SIZE);
    /* the log area size must be an integral multiple of erase minimum size. */
    EF_ASSERT(LOG_AREA_SIZE % EF_ERASE_MIN_SIZE == 0);
    /* the log area size must be more than twice of EF_ERASE_MIN_SIZE */
    EF_ASSERT(LOG_AREA_SIZE / EF_ERASE_MIN_SIZE >= 2);

    log_area_start_addr = EF_START_ADDR + ENV_AREA_SIZE;

    /* find the log store start address and end address */
    find_start_and_end_addr();
    /* initialize OK */
    init_ok = 1;

    return result;
}

/**
 * Get flash sector current status.
 *
 * @param addr sector address, this function will auto calculate the sector header address by this address.
 *
 * @return the flash sector current status
 */
static SectorStatus get_sector_status(uint32_t addr) 
{
    uint32_t header_buf[LOG_SECTOR_HEADER_WORD_SIZE] = {0}, header_addr = 0;
    uint32_t sector_header_magic = 0;
    uint32_t status_full_magic = 0, status_use_magic = 0;

    /* calculate the sector header address */
    header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1));

    if (ef_port_read(header_addr, header_buf, sizeof(header_buf)) == EF_NO_ERR) 
    {
        sector_header_magic = header_buf[SECTOR_HEADER_MAGIC_INDEX];
        status_use_magic = header_buf[SECTOR_HEADER_USING_INDEX];
        status_full_magic = header_buf[SECTOR_HEADER_FULL_INDEX];
    } else {
        EF_DEBUG("Error: Read sector header data error.\n");
        return SECTOR_STATUS_HEADER_ERROR;
    }

    /* compare header magic code */
    if(sector_header_magic == LOG_SECTOR_MAGIC)
    {
        if((status_use_magic == SECTOR_STATUS_MAGIC_EMPUT) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) {
            return SECTOR_STATUS_EMPUT;
        } else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_EMPUT)) {
             return SECTOR_STATUS_USING;
        } else if((status_use_magic == SECTOR_STATUS_MAGIC_USING) && (status_full_magic == SECTOR_STATUS_MAGIC_FULL)) {
             return SECTOR_STATUS_FULL;
        } else {
            return SECTOR_STATUS_HEADER_ERROR;
        }
    } 
    else 
    {
        return SECTOR_STATUS_HEADER_ERROR;
    }

}

/**
 * Write flash sector current status.
 *
 * @param addr sector address, this function will auto calculate the sector header address by this address.
 * @param status sector cur status
 *
 * @return result
 */
static EfErrCode write_sector_status(uint32_t addr, SectorStatus status) {
    uint32_t header, header_addr = 0;

    /* calculate the sector header address */
    header_addr = addr & (~(EF_ERASE_MIN_SIZE - 1));

    /* calculate the sector staus magic */
    switch (status) {
    case SECTOR_STATUS_EMPUT: {
        header = LOG_SECTOR_MAGIC;
        return ef_port_write(header_addr, &header, sizeof(header));
    }
    case SECTOR_STATUS_USING: {
        header = SECTOR_STATUS_MAGIC_USING;
        return ef_port_write(header_addr + sizeof(header), &header, sizeof(header));
    }
    case SECTOR_STATUS_FULL: {
        header = SECTOR_STATUS_MAGIC_FULL;
        return ef_port_write(header_addr + sizeof(header) * 2, &header, sizeof(header));
    }
    default:
        return EF_WRITE_ERR;
    }
}

/**
 * Find the current flash sector using end address by continuous 0xFF.
 *
 * @param addr sector address
 *
 * @return current flash sector using end address
 */
static uint32_t find_sec_using_end_addr(uint32_t addr) {
/* read section data buffer size */
#define READ_BUF_SIZE                32

    uint32_t sector_start = addr, data_start = addr, continue_ff = 0, read_buf_size = 0, i;
    uint8_t buf[READ_BUF_SIZE];

    EF_ASSERT(READ_BUF_SIZE % 4 == 0);
    /* calculate the sector start and data start address */
    sector_start = addr & (~(EF_ERASE_MIN_SIZE - 1));
    data_start = sector_start + LOG_SECTOR_HEADER_SIZE;

    /* counts continuous 0xFF which is end of sector */
    while (data_start < sector_start + EF_ERASE_MIN_SIZE) {
        if (data_start + READ_BUF_SIZE < sector_start + EF_ERASE_MIN_SIZE) {
            read_buf_size = READ_BUF_SIZE;
        } else {
            read_buf_size = sector_start + EF_ERASE_MIN_SIZE - data_start;
        }
        ef_port_read(data_start, (uint32_t *)buf, read_buf_size);
        for (i = 0; i < read_buf_size; i++) {
            if (buf[i] == 0xFF) {
                continue_ff++;
            } else {
                continue_ff = 0;
            }
        }
        data_start += read_buf_size;
    }
    /* calculate current flash sector using end address */
    if (continue_ff >= EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) {
        /* from 0 to sec_size all sector is 0xFF, so the sector is empty */
        return sector_start + LOG_SECTOR_HEADER_SIZE;
    } else if (continue_ff >= 4) {
        /* form end_addr - 4 to sec_size length all area is 0xFF, so it's used part of the sector.
         * the address must be word alignment. */
        if (continue_ff % 4 != 0) {
            continue_ff = (continue_ff / 4 + 1) * 4;
        }
        return sector_start + EF_ERASE_MIN_SIZE - continue_ff;
    } else {
        /* all sector not has continuous 0xFF, so the sector is full */
        return sector_start + EF_ERASE_MIN_SIZE;
    }
}

/**
 * Find the log store start address and end address.
 * It's like a ring buffer implemented on flash.
 * The flash log area can be in two states depending on start address and end address:
 *                       state 1                                state 2
 *                   |============|                         |============|
 * log area start--> |############| <-- start address       |############| <-- end address
 *                   |############|                         |   empty    |
 *                   |------------|                         |------------|
 *                   |############|                         |############| <-- start address
 *                   |############|                         |############|
 *                   |------------|                         |------------|
 *                   |     .      |                         |     .      |
 *                   |     .      |                         |     .      |
 *                   |     .      |                         |     .      |
 *                   |------------|                         |------------|
 *                   |############| <-- end address         |############|
 *                   |   empty    |                         |############|
 *  log area end --> |============|                         |============|
 *
 *  LOG_AREA_SIZE = log area end - log area star
 *
 */
static void find_start_and_end_addr(void) 
{
    size_t cur_size = 0;
    SectorStatus cur_sec_status, last_sec_status;
    uint32_t cur_using_sec_addr = 0;
    /* all status sector counts */
    size_t empty_sec_counts = 0, using_sec_counts = 0, full_sector_counts = 0;
    /* total sector number */
    size_t total_sec_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;
    /* see comment of find_start_and_end_addr function */
    uint8_t cur_log_sec_state = 0;

    /* get the first sector status */
    cur_sec_status = get_sector_status(log_area_start_addr);
    last_sec_status = cur_sec_status;

    for (cur_size = EF_ERASE_MIN_SIZE; cur_size < LOG_AREA_SIZE; cur_size += EF_ERASE_MIN_SIZE) 
    {
        /* get current sector status */
        cur_sec_status = get_sector_status(log_area_start_addr + cur_size);
        /* compare last and current status */
        switch (last_sec_status) 
        {
            case SECTOR_STATUS_EMPUT: 
            {
                switch (cur_sec_status) 
                {
                    case SECTOR_STATUS_EMPUT:
                        break;
                    case SECTOR_STATUS_USING:
                        EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
                        ef_log_clean();
                        return;
                    case SECTOR_STATUS_FULL:
                        EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
                        ef_log_clean();
                        return;
                    
                    case SECTOR_STATUS_HEADER_ERROR:
                        {}break;
                }
                empty_sec_counts++;
                break;
            }
            
            case SECTOR_STATUS_USING: 
            {
                switch (cur_sec_status) 
                {
                    case SECTOR_STATUS_EMPUT:
                        /* like state 1 */
                        cur_log_sec_state = 1;
                        log_start_addr = log_area_start_addr;
                        cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
                        break;
                    case SECTOR_STATUS_USING:
                        EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
                        ef_log_clean();
                        return;
                    case SECTOR_STATUS_FULL:
                        /* like state 2 */
                        cur_log_sec_state = 2;
                        log_start_addr = log_area_start_addr + cur_size;
                        cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
                        break;
                    
                    case SECTOR_STATUS_HEADER_ERROR:
                        {}break;
                }
                using_sec_counts++;
                break;
            }
            
            case SECTOR_STATUS_FULL: 
            {
                switch (cur_sec_status) 
                {
                    case SECTOR_STATUS_EMPUT:
                        /* like state 1 */
                        if (cur_log_sec_state == 2) {
                            EF_DEBUG("Error: Log area error! Now will clean all log area.\n");
                            ef_log_clean();
                            return;
                        } else {
                            cur_log_sec_state = 1;
                            log_start_addr = log_area_start_addr;
                            log_end_addr = log_area_start_addr + cur_size;
                            cur_using_sec_addr = log_area_start_addr + cur_size - EF_ERASE_MIN_SIZE;
                        }
                        break;
                        
                    case SECTOR_STATUS_USING:
                        if(total_sec_num <= 2) {
                            /* like state 1 */
                            cur_log_sec_state = 1;
                            log_start_addr = log_area_start_addr;
                            cur_using_sec_addr = log_area_start_addr + cur_size;
                        } else {
                            /* like state 2 when the sector is the last one */
                            if (cur_size + EF_ERASE_MIN_SIZE >= LOG_AREA_SIZE) {
                                cur_log_sec_state = 2;
                                log_start_addr = get_next_flash_sec_addr(log_area_start_addr + cur_size);
                                cur_using_sec_addr = log_area_start_addr + cur_size;
                            }
                        }
                        break;
                        
                    case SECTOR_STATUS_FULL:
                        break;
                    
                    case SECTOR_STATUS_HEADER_ERROR:
                        {}break;
                }
                full_sector_counts++;
                break;
            }
            
            case SECTOR_STATUS_HEADER_ERROR:
                EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n");
                ef_log_clean();
                return;
        }
        last_sec_status = cur_sec_status;
    }

    /* the last sector status counts */
    if (cur_sec_status == SECTOR_STATUS_EMPUT) {
        empty_sec_counts++;
    } else if (cur_sec_status == SECTOR_STATUS_USING) {
        using_sec_counts++;
    } else if (cur_sec_status == SECTOR_STATUS_FULL) {
        full_sector_counts++;
    } else if (cur_sec_status == SECTOR_STATUS_HEADER_ERROR) {
        EF_DEBUG("Error: Log sector header error! Now will clean all log area.\n");
        ef_log_clean();
        return;
    }

    if (using_sec_counts != 1) {
        /* this state is almost impossible */
        EF_DEBUG("Error: There must be only one sector status is USING! Now will clean all log area.\n");
        ef_log_clean();
    } else {
        /* find the end address */
        log_end_addr = find_sec_using_end_addr(cur_using_sec_addr);
    }

}

/**
 * Get log used flash total size.
 *
 * @return log used flash total size. @note NOT contain sector headers
 */
size_t ef_log_get_used_size(void) {
    size_t header_total_num = 0, physical_size = 0;
    /* must be call this function after initialize OK */
    if (!init_ok) {
        return 0;
    }

    if (log_start_addr < log_end_addr) {
        physical_size = log_end_addr - log_start_addr;
    } else {
        physical_size = LOG_AREA_SIZE - (log_start_addr - log_end_addr);
    }

    header_total_num = physical_size / EF_ERASE_MIN_SIZE + 1;

    return physical_size - header_total_num * LOG_SECTOR_HEADER_SIZE;
}

/**
 * Sequential reading log data. It will ignore sector headers.
 *
 * @param addr address
 * @param log log buffer
 * @param size log size, not contain sector headers.
 *
 * @return result
 */
static EfErrCode log_seq_read(uint32_t addr, uint32_t *log, size_t size) {
    EfErrCode result = EF_NO_ERR;
    size_t read_size = 0, read_size_temp = 0;

    while (size) {
        /* move to sector data address */
        if ((addr + read_size) % EF_ERASE_MIN_SIZE == 0) {
            addr += LOG_SECTOR_HEADER_SIZE;
        }
        /* calculate current sector last data size */
        read_size_temp = EF_ERASE_MIN_SIZE - (addr % EF_ERASE_MIN_SIZE);
        if (size < read_size_temp) {
            read_size_temp = size;
        }
        result = ef_port_read(addr + read_size, log + read_size / 4, read_size_temp);
        if (result != EF_NO_ERR) {
            return result;
        }
        read_size += read_size_temp;
        size -= read_size_temp;
    }

    return result;
}

/**
 * Calculate flash physical address by log index.
 *
 * @param index log index
 *
 * @return flash physical address
 */
static uint32_t log_index2addr(size_t index) {
    size_t header_total_offset = 0;
    /* total include sector number */
    size_t sector_num = index / (EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE) + 1;

    header_total_offset = sector_num * LOG_SECTOR_HEADER_SIZE;
    if (log_start_addr < log_end_addr) {
        return log_start_addr + index + header_total_offset;
    } else {
        if (log_start_addr + index + header_total_offset < log_area_start_addr + LOG_AREA_SIZE) {
            return log_start_addr + index + header_total_offset;
        } else {
            return log_start_addr + index + header_total_offset - LOG_AREA_SIZE;

        }
    }
}

/**
 * Read log from flash.
 *
 * @param index index for saved log.
 *        Minimum index is 0.
 *        Maximum index is ef_log_get_used_size() - 1.
 * @param log the log which will read from flash
 * @param size read bytes size
 *
 * @return result
 */
EfErrCode ef_log_read(size_t index, uint32_t *log, size_t size) {
    EfErrCode result = EF_NO_ERR;
    size_t cur_using_size = ef_log_get_used_size();
    size_t read_size_temp = 0;
    size_t header_total_num = 0;

    if (!size) {
        return result;
    }

    EF_ASSERT(size % 4 == 0);
    EF_ASSERT(index < cur_using_size);

    if (index + size > cur_using_size) {
        EF_DEBUG("Warning: Log read size out of bound. Cut read size.\n");
        size = cur_using_size - index;
    }
    /* must be call this function after initialize OK */
    if (!init_ok) {
        return EF_ENV_INIT_FAILED;
    }

    if (log_start_addr < log_end_addr) {
        log_seq_read(log_index2addr(index), log, size);
    } else {
        if (log_index2addr(index) + size <= log_area_start_addr + LOG_AREA_SIZE) {
            /*                          Flash log area
             *                         |--------------|
             * log_area_start_addr --> |##############|
             *                         |##############|
             *                         |##############|
             *                         |--------------|
             *                         |##############|
             *                         |##############|
             *                         |##############| <-- log_end_addr
             *                         |--------------|
             *      log_start_addr --> |##############|
             *          read start --> |**************| <-- read end
             *                         |##############|
             *                         |--------------|
             *
             * read from (log_start_addr + log_index2addr(index)) to (log_start_addr + index + log_index2addr(index))
             */
            result = log_seq_read(log_index2addr(index), log, size);
        } else if (log_index2addr(index) < log_area_start_addr + LOG_AREA_SIZE) {
            /*                          Flash log area
             *                         |--------------|
             * log_area_start_addr --> |**************| <-- read end
             *                         |##############|
             *                         |##############|
             *                         |--------------|
             *                         |##############|
             *                         |##############|
             *                         |##############| <-- log_end_addr
             *                         |--------------|
             *      log_start_addr --> |##############|
             *          read start --> |**************|
             *                         |**************|
             *                         |--------------|
             * read will by 2 steps
             * step1: read from (log_start_addr + log_index2addr(index)) to flash log area end address
             * step2: read from flash log area start address to read size's end address
             */
            read_size_temp = (log_area_start_addr + LOG_AREA_SIZE) - log_index2addr(index);
            header_total_num = read_size_temp / EF_ERASE_MIN_SIZE;
            /* Minus some ignored bytes */
            read_size_temp -= header_total_num * LOG_SECTOR_HEADER_SIZE;
            result = log_seq_read(log_index2addr(index), log, read_size_temp);
            if (result == EF_NO_ERR) {
                result = log_seq_read(log_area_start_addr, log + read_size_temp / 4, size - read_size_temp);
            }
        } else {
            /*                          Flash log area
             *                         |--------------|
             * log_area_start_addr --> |##############|
             *          read start --> |**************|
             *                         |**************| <-- read end
             *                         |--------------|
             *                         |##############|
             *                         |##############|
             *                         |##############| <-- log_end_addr
             *                         |--------------|
             *      log_start_addr --> |##############|
             *                         |##############|
             *                         |##############|
             *                         |--------------|
             * read from (log_start_addr + log_index2addr(index) - LOG_AREA_SIZE) to read size's end address
             */
            result = log_seq_read(log_index2addr(index) - LOG_AREA_SIZE, log, size);
        }
    }

    return result;
}

/**
 * Write log to flash.
 *
 * @param log the log which will be write to flash
 * @param size write bytes size
 *
 * @return result
 */
EfErrCode ef_log_write(const uint32_t *log, size_t size) 
{
    EfErrCode result = EF_NO_ERR;
    size_t write_size = 0, writable_size = 0;
    uint32_t write_addr = log_end_addr, erase_addr;
    SectorStatus sector_status;

    EF_ASSERT(size % 4 == 0);
    /* must be call this function after initialize OK */
    if (!init_ok) {
        return EF_ENV_INIT_FAILED;
    }

    if ((sector_status = get_sector_status(write_addr)) == SECTOR_STATUS_HEADER_ERROR) {
        return EF_WRITE_ERR;
    }
    /* write some log when current sector status is USING and EMPTY */
    if ((sector_status == SECTOR_STATUS_USING) || (sector_status == SECTOR_STATUS_EMPUT)) {
        /* write the already erased but not used area */
        writable_size = EF_ERASE_MIN_SIZE - ((write_addr - log_area_start_addr) % EF_ERASE_MIN_SIZE);
        if (size >= writable_size) {
            result = ef_port_write(write_addr, log, writable_size);
            if (result != EF_NO_ERR) {
                goto exit;
            }
            /* change the current sector status to FULL */
            result = write_sector_status(write_addr, SECTOR_STATUS_FULL);
            if (result != EF_NO_ERR) {
                goto exit;
            }
            write_size += writable_size;
        } else {
            result = ef_port_write(write_addr, log, size);
            log_end_addr = write_addr + size;
            goto exit;
        }
    }
    /* erase and write remain log */
    while (1) 
    {
        /* calculate next available sector address */
        erase_addr = write_addr = get_next_flash_sec_addr(write_addr - 4);
        /* move the flash log start address to next available sector address */
        if (log_start_addr == erase_addr) {
            log_start_addr = get_next_flash_sec_addr(log_start_addr);
        }
        /* erase sector */
        result = ef_port_erase(erase_addr, EF_ERASE_MIN_SIZE);
        if (result != EF_NO_ERR) {
            goto exit;
        }
        /* change the sector status to EMPTY and USING when write begin sector start address */
        result = write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
        result = write_sector_status(write_addr, SECTOR_STATUS_USING);
        if (result == EF_NO_ERR) {
            write_addr += LOG_SECTOR_HEADER_SIZE;
        } else {
            goto exit;
        }
        /* calculate current sector writable data size */
        writable_size = EF_ERASE_MIN_SIZE - LOG_SECTOR_HEADER_SIZE;
        if (size - write_size >= writable_size) {
            result = ef_port_write(write_addr, log + write_size / 4, writable_size);
            if (result != EF_NO_ERR) {
                goto exit;
            }
            /* change the current sector status to FULL */
            result = write_sector_status(write_addr, SECTOR_STATUS_FULL);
            if (result != EF_NO_ERR) {
                goto exit;
            }
            log_end_addr = write_addr + writable_size;
            write_size += writable_size;
            write_addr += writable_size;
        } else {
            result = ef_port_write(write_addr, log + write_size / 4, size - write_size);
            if (result != EF_NO_ERR) {
                goto exit;
            }
            log_end_addr = write_addr + (size - write_size);
            break;
        }
    }

exit:
    return result;
}

/**
 * Get next flash sector address.The log total sector like ring buffer which implement by flash.
 *
 * @param cur_addr cur flash address
 *
 * @return next flash sector address
 */
static uint32_t get_next_flash_sec_addr(uint32_t cur_addr) {
    size_t cur_sec_id = (cur_addr - log_area_start_addr) / EF_ERASE_MIN_SIZE;
    size_t sec_total_num = LOG_AREA_SIZE / EF_ERASE_MIN_SIZE;

    if (cur_sec_id + 1 >= sec_total_num) {
        /* return to ring head */
        return log_area_start_addr;
    } else {
        return log_area_start_addr + (cur_sec_id + 1) * EF_ERASE_MIN_SIZE;
    }
}

/**
 * Clean all log which in flash.
 *
 * @return result
 */
EfErrCode ef_log_clean(void) 
{
    EfErrCode result = EF_NO_ERR;
    uint32_t write_addr = log_area_start_addr;

    /* clean address */
    log_start_addr = log_area_start_addr;
    log_end_addr = log_start_addr + LOG_SECTOR_HEADER_SIZE;
    /* erase log flash area */
    result = ef_port_erase(log_area_start_addr, LOG_AREA_SIZE);
    if (result != EF_NO_ERR) {
        goto exit;
    }
    /* setting first sector is EMPTY to USING */
    write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
    write_sector_status(write_addr, SECTOR_STATUS_USING);
    if (result != EF_NO_ERR) {
        goto exit;
    }
    write_addr += EF_ERASE_MIN_SIZE;
    /* add sector header */
    while (1) 
    {
        WDG_Refresh();
        write_sector_status(write_addr, SECTOR_STATUS_EMPUT);
        if (result != EF_NO_ERR) {
            goto exit;
        }
        write_addr += EF_ERASE_MIN_SIZE;
        if (write_addr >= log_area_start_addr + LOG_AREA_SIZE) 
        {
            break;
        }
    }

exit:
    return result;
}

uint8_t ef_log_buf[48];

void log_write(uint8_t lvl,uint8_t tag,int16_t v1,int16_t v2,int32_t v3,uint8_t *pbuf,uint16_t len)
{
    uint16_t point;
    uint16_t i;
    
    uint32_t time;
    
    point = 0;
    
    
    if(len > 18)
        len = 18;
    
    time = iProfile.sys_status.Date.UTC_Date ;
    
    ef_log_buf[point++] = ((time>>0)&0xFF);
    ef_log_buf[point++] = ((time>>8)&0xFF);
    ef_log_buf[point++] = ((time>>16)&0xFF);
    ef_log_buf[point++] = ((time>>24)&0xFF);
    
    ef_log_buf[point++] = lvl;
    ef_log_buf[point++] = tag;
    
    ef_log_buf[point++] = ((v1>>0)&0xFF);
    ef_log_buf[point++] = ((v1>>8)&0xFF);
    
    ef_log_buf[point++] = ((v2>>0)&0xFF);
    ef_log_buf[point++] = ((v2>>8)&0xFF);
    
    ef_log_buf[point++] = ((v3>>0)&0xFF);
    ef_log_buf[point++] = ((v3>>8)&0xFF);
    ef_log_buf[point++] = ((v3>>16)&0xFF);
    ef_log_buf[point++] = ((v3>>24)&0xFF);
       
    for(i=0;i<len;i++)
    {
        ef_log_buf[point++] = pbuf[i];
    }
    //point = EF_ALIGN(point,4);
    ef_log_write((uint32_t *)ef_log_buf,32);
}







/********************************(C)COPYRIGHT 2016 RSEStudio ****************************/
/*****************************************END OF FILE************************************/

